Commit 829f3b94 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pstore-v5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull pstore updates from Kees Cook:
 "Fixes and new features for pstore.

  This is a pretty big set of changes (relative to past pstore pulls),
  but it has been in -next for a while. The biggest change here is the
  ability to support a block device as a pstore backend, which has been
  desired for a while. A lot of additional fixes and refactorings are
  also included, mostly in support of the new features.

   - refactor pstore locking for safer module unloading (Kees Cook)

   - remove orphaned records from pstorefs when backend unloaded (Kees
     Cook)

   - refactor dump_oops parameter into max_reason (Pavel Tatashin)

   - introduce pstore/zone for common code for contiguous storage
     (WeiXiong Liao)

   - introduce pstore/blk for block device backend (WeiXiong Liao)

   - introduce mtd backend (WeiXiong Liao)"

* tag 'pstore-v5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (35 commits)
  mtd: Support kmsg dumper based on pstore/blk
  pstore/blk: Introduce "best_effort" mode
  pstore/blk: Support non-block storage devices
  pstore/blk: Provide way to query pstore configuration
  pstore/zone: Provide way to skip "broken" zone for MTD devices
  Documentation: Add details for pstore/blk
  pstore/zone,blk: Add ftrace frontend support
  pstore/zone,blk: Add console frontend support
  pstore/zone,blk: Add support for pmsg frontend
  pstore/blk: Introduce backend for block devices
  pstore/zone: Introduce common layer to manage storage zones
  ramoops: Add "max-reason" optional field to ramoops DT node
  pstore/ram: Introduce max_reason and convert dump_oops
  pstore/platform: Pass max_reason to kmesg dump
  printk: Introduce kmsg_dump_reason_str()
  printk: honor the max_reason field in kmsg_dumper
  printk: Collapse shutdown types into a single dump reason
  pstore/ftrace: Provide ftrace log merging routine
  pstore/ram: Refactor ftrace buffer merging
  pstore/ram: Refactor DT size parsing
  ...
parents 81e8c10d 78c08247
.. SPDX-License-Identifier: GPL-2.0
pstore block oops/panic logger
==============================
Introduction
------------
pstore block (pstore/blk) is an oops/panic logger that writes its logs to a
block device and non-block device before the system crashes. You can get
these log files by mounting pstore filesystem like::
mount -t pstore pstore /sys/fs/pstore
pstore block concepts
---------------------
pstore/blk provides efficient configuration method for pstore/blk, which
divides all configurations into two parts, configurations for user and
configurations for driver.
Configurations for user determine how pstore/blk works, such as pmsg_size,
kmsg_size and so on. All of them support both Kconfig and module parameters,
but module parameters have priority over Kconfig.
Configurations for driver are all about block device and non-block device,
such as total_size of block device and read/write operations.
Configurations for user
-----------------------
All of these configurations support both Kconfig and module parameters, but
module parameters have priority over Kconfig.
Here is an example for module parameters::
pstore_blk.blkdev=179:7 pstore_blk.kmsg_size=64
The detail of each configurations may be of interest to you.
blkdev
~~~~~~
The block device to use. Most of the time, it is a partition of block device.
It's required for pstore/blk. It is also used for MTD device.
It accepts the following variants for block device:
1. <hex_major><hex_minor> device number in hexadecimal represents itself; no
leading 0x, for example b302.
#. /dev/<disk_name> represents the device number of disk
#. /dev/<disk_name><decimal> represents the device number of partition - device
number of disk plus the partition number
#. /dev/<disk_name>p<decimal> - same as the above; this form is used when disk
name of partitioned disk ends with a digit.
#. PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF represents the unique id of
a partition if the partition table provides it. The UUID may be either an
EFI/GPT UUID, or refer to an MSDOS partition using the format SSSSSSSS-PP,
where SSSSSSSS is a zero-filled hex representation of the 32-bit
"NT disk signature", and PP is a zero-filled hex representation of the
1-based partition number.
#. PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to a
partition with a known unique id.
#. <major>:<minor> major and minor number of the device separated by a colon.
It accepts the following variants for MTD device:
1. <device name> MTD device name. "pstore" is recommended.
#. <device number> MTD device number.
kmsg_size
~~~~~~~~~
The chunk size in KB for oops/panic front-end. It **MUST** be a multiple of 4.
It's optional if you do not care oops/panic log.
There are multiple chunks for oops/panic front-end depending on the remaining
space except other pstore front-ends.
pstore/blk will log to oops/panic chunks one by one, and always overwrite the
oldest chunk if there is no more free chunk.
pmsg_size
~~~~~~~~~
The chunk size in KB for pmsg front-end. It **MUST** be a multiple of 4.
It's optional if you do not care pmsg log.
Unlike oops/panic front-end, there is only one chunk for pmsg front-end.
Pmsg is a user space accessible pstore object. Writes to */dev/pmsg0* are
appended to the chunk. On reboot the contents are available in
*/sys/fs/pstore/pmsg-pstore-blk-0*.
console_size
~~~~~~~~~~~~
The chunk size in KB for console front-end. It **MUST** be a multiple of 4.
It's optional if you do not care console log.
Similar to pmsg front-end, there is only one chunk for console front-end.
All log of console will be appended to the chunk. On reboot the contents are
available in */sys/fs/pstore/console-pstore-blk-0*.
ftrace_size
~~~~~~~~~~~
The chunk size in KB for ftrace front-end. It **MUST** be a multiple of 4.
It's optional if you do not care console log.
Similar to oops front-end, there are multiple chunks for ftrace front-end
depending on the count of cpu processors. Each chunk size is equal to
ftrace_size / processors_count.
All log of ftrace will be appended to the chunk. On reboot the contents are
combined and available in */sys/fs/pstore/ftrace-pstore-blk-0*.
Persistent function tracing might be useful for debugging software or hardware
related hangs. Here is an example of usage::
# mount -t pstore pstore /sys/fs/pstore
# mount -t debugfs debugfs /sys/kernel/debug/
# echo 1 > /sys/kernel/debug/pstore/record_ftrace
# reboot -f
[...]
# mount -t pstore pstore /sys/fs/pstore
# tail /sys/fs/pstore/ftrace-pstore-blk-0
CPU:0 ts:5914676 c0063828 c0063b94 call_cpuidle <- cpu_startup_entry+0x1b8/0x1e0
CPU:0 ts:5914678 c039ecdc c006385c cpuidle_enter_state <- call_cpuidle+0x44/0x48
CPU:0 ts:5914680 c039e9a0 c039ecf0 cpuidle_enter_freeze <- cpuidle_enter_state+0x304/0x314
CPU:0 ts:5914681 c0063870 c039ea30 sched_idle_set_state <- cpuidle_enter_state+0x44/0x314
CPU:1 ts:5916720 c0160f59 c015ee04 kernfs_unmap_bin_file <- __kernfs_remove+0x140/0x204
CPU:1 ts:5916721 c05ca625 c015ee0c __mutex_lock_slowpath <- __kernfs_remove+0x148/0x204
CPU:1 ts:5916723 c05c813d c05ca630 yield_to <- __mutex_lock_slowpath+0x314/0x358
CPU:1 ts:5916724 c05ca2d1 c05ca638 __ww_mutex_lock <- __mutex_lock_slowpath+0x31c/0x358
max_reason
~~~~~~~~~~
Limiting which kinds of kmsg dumps are stored can be controlled via
the ``max_reason`` value, as defined in include/linux/kmsg_dump.h's
``enum kmsg_dump_reason``. For example, to store both Oopses and Panics,
``max_reason`` should be set to 2 (KMSG_DUMP_OOPS), to store only Panics
``max_reason`` should be set to 1 (KMSG_DUMP_PANIC). Setting this to 0
(KMSG_DUMP_UNDEF), means the reason filtering will be controlled by the
``printk.always_kmsg_dump`` boot param: if unset, it'll be KMSG_DUMP_OOPS,
otherwise KMSG_DUMP_MAX.
Configurations for driver
-------------------------
Only a block device driver cares about these configurations. A block device
driver uses ``register_pstore_blk`` to register to pstore/blk.
.. kernel-doc:: fs/pstore/blk.c
:identifiers: register_pstore_blk
A non-block device driver uses ``register_pstore_device`` with
``struct pstore_device_info`` to register to pstore/blk.
.. kernel-doc:: fs/pstore/blk.c
:identifiers: register_pstore_device
.. kernel-doc:: include/linux/pstore_blk.h
:identifiers: pstore_device_info
Compression and header
----------------------
Block device is large enough for uncompressed oops data. Actually we do not
recommend data compression because pstore/blk will insert some information into
the first line of oops/panic data. For example::
Panic: Total 16 times
It means that it's OOPS|Panic for the 16th time since the first booting.
Sometimes the number of occurrences of oops|panic since the first booting is
important to judge whether the system is stable.
The following line is inserted by pstore filesystem. For example::
Oops#2 Part1
It means that it's OOPS for the 2nd time on the last boot.
Reading the data
----------------
The dump data can be read from the pstore filesystem. The format for these
files is ``dmesg-pstore-blk-[N]`` for oops/panic front-end,
``pmsg-pstore-blk-0`` for pmsg front-end and so on. The timestamp of the
dump file records the trigger time. To delete a stored record from block
device, simply unlink the respective pstore file.
Attentions in panic read/write APIs
-----------------------------------
If on panic, the kernel is not going to run for much longer, the tasks will not
be scheduled and most kernel resources will be out of service. It
looks like a single-threaded program running on a single-core computer.
The following points require special attention for panic read/write APIs:
1. Can **NOT** allocate any memory.
If you need memory, just allocate while the block driver is initializing
rather than waiting until the panic.
#. Must be polled, **NOT** interrupt driven.
No task schedule any more. The block driver should delay to ensure the write
succeeds, but NOT sleep.
#. Can **NOT** take any lock.
There is no other task, nor any shared resource; you are safe to break all
locks.
#. Just use CPU to transfer.
Do not use DMA to transfer unless you are sure that DMA will not keep lock.
#. Control registers directly.
Please control registers directly rather than use Linux kernel resources.
Do I/O map while initializing rather than wait until a panic occurs.
#. Reset your block device and controller if necessary.
If you are not sure of the state of your block device and controller when
a panic occurs, you are safe to stop and reset them.
pstore/blk supports psblk_blkdev_info(), which is defined in
*linux/pstore_blk.h*, to get information of using block device, such as the
device number, sector count and start sector of the whole disk.
pstore block internals
----------------------
For developer reference, here are all the important structures and APIs:
.. kernel-doc:: fs/pstore/zone.c
:internal:
.. kernel-doc:: include/linux/pstore_zone.h
:internal:
.. kernel-doc:: fs/pstore/blk.c
:export:
.. kernel-doc:: include/linux/pstore_blk.h
:internal:
...@@ -32,11 +32,17 @@ memory to be mapped strongly ordered, and atomic operations on strongly ordered ...@@ -32,11 +32,17 @@ memory to be mapped strongly ordered, and atomic operations on strongly ordered
memory are implementation defined, and won't work on many ARMs such as omaps. memory are implementation defined, and won't work on many ARMs such as omaps.
The memory area is divided into ``record_size`` chunks (also rounded down to The memory area is divided into ``record_size`` chunks (also rounded down to
power of two) and each oops/panic writes a ``record_size`` chunk of power of two) and each kmesg dump writes a ``record_size`` chunk of
information. information.
Dumping both oopses and panics can be done by setting 1 in the ``dump_oops`` Limiting which kinds of kmsg dumps are stored can be controlled via
variable while setting 0 in that variable dumps only the panics. the ``max_reason`` value, as defined in include/linux/kmsg_dump.h's
``enum kmsg_dump_reason``. For example, to store both Oopses and Panics,
``max_reason`` should be set to 2 (KMSG_DUMP_OOPS), to store only Panics
``max_reason`` should be set to 1 (KMSG_DUMP_PANIC). Setting this to 0
(KMSG_DUMP_UNDEF), means the reason filtering will be controlled by the
``printk.always_kmsg_dump`` boot param: if unset, it'll be KMSG_DUMP_OOPS,
otherwise KMSG_DUMP_MAX.
The module uses a counter to record multiple dumps but the counter gets reset The module uses a counter to record multiple dumps but the counter gets reset
on restart (i.e. new dumps after the restart will overwrite old ones). on restart (i.e. new dumps after the restart will overwrite old ones).
...@@ -90,7 +96,7 @@ Setting the ramoops parameters can be done in several different manners: ...@@ -90,7 +96,7 @@ Setting the ramoops parameters can be done in several different manners:
.mem_address = <...>, .mem_address = <...>,
.mem_type = <...>, .mem_type = <...>,
.record_size = <...>, .record_size = <...>,
.dump_oops = <...>, .max_reason = <...>,
.ecc = <...>, .ecc = <...>,
}; };
......
...@@ -30,7 +30,7 @@ Optional properties: ...@@ -30,7 +30,7 @@ Optional properties:
- ecc-size: enables ECC support and specifies ECC buffer size in bytes - ecc-size: enables ECC support and specifies ECC buffer size in bytes
(defaults to 0: no ECC) (defaults to 0: no ECC)
- record-size: maximum size in bytes of each dump done on oops/panic - record-size: maximum size in bytes of each kmsg dump.
(defaults to 0: disabled) (defaults to 0: disabled)
- console-size: size in bytes of log buffer reserved for kernel messages - console-size: size in bytes of log buffer reserved for kernel messages
...@@ -45,7 +45,16 @@ Optional properties: ...@@ -45,7 +45,16 @@ Optional properties:
- unbuffered: if present, use unbuffered mappings to map the reserved region - unbuffered: if present, use unbuffered mappings to map the reserved region
(defaults to buffered mappings) (defaults to buffered mappings)
- no-dump-oops: if present, only dump panics (defaults to panics and oops) - max-reason: if present, sets maximum type of kmsg dump reasons to store
(defaults to 2: log Oopses and Panics). This can be set to INT_MAX to
store all kmsg dumps. See include/linux/kmsg_dump.h KMSG_DUMP_* for other
kmsg dump reason values. Setting this to 0 (KMSG_DUMP_UNDEF), means the
reason filtering will be controlled by the printk.always_kmsg_dump boot
param: if unset, it will be KMSG_DUMP_OOPS, otherwise KMSG_DUMP_MAX.
- no-dump-oops: deprecated, use max_reason instead. If present, and
max_reason is not specified, it is equivalent to max_reason = 1
(KMSG_DUMP_PANIC).
- flags: if present, pass ramoops behavioral flags (defaults to 0, - flags: if present, pass ramoops behavioral flags (defaults to 0,
see include/linux/pstore_ram.h RAMOOPS_FLAG_* for flag values). see include/linux/pstore_ram.h RAMOOPS_FLAG_* for flag values).
...@@ -13715,6 +13715,7 @@ M: Tony Luck <tony.luck@intel.com> ...@@ -13715,6 +13715,7 @@ M: Tony Luck <tony.luck@intel.com>
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore
F: Documentation/admin-guide/ramoops.rst F: Documentation/admin-guide/ramoops.rst
F: Documentation/admin-guide/pstore-blk.rst
F: Documentation/devicetree/bindings/reserved-memory/ramoops.txt F: Documentation/devicetree/bindings/reserved-memory/ramoops.txt
F: drivers/acpi/apei/erst.c F: drivers/acpi/apei/erst.c
F: drivers/firmware/efi/efi-pstore.c F: drivers/firmware/efi/efi-pstore.c
......
...@@ -655,9 +655,7 @@ static void oops_to_nvram(struct kmsg_dumper *dumper, ...@@ -655,9 +655,7 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
int rc = -1; int rc = -1;
switch (reason) { switch (reason) {
case KMSG_DUMP_RESTART: case KMSG_DUMP_SHUTDOWN:
case KMSG_DUMP_HALT:
case KMSG_DUMP_POWEROFF:
/* These are almost always orderly shutdowns. */ /* These are almost always orderly shutdowns. */
return; return;
case KMSG_DUMP_OOPS: case KMSG_DUMP_OOPS:
......
...@@ -170,6 +170,16 @@ config MTD_OOPS ...@@ -170,6 +170,16 @@ config MTD_OOPS
buffer in a flash partition where it can be read back at some buffer in a flash partition where it can be read back at some
later point. later point.
config MTD_PSTORE
tristate "Log panic/oops to an MTD buffer based on pstore"
depends on PSTORE_BLK
help
This enables panic and oops messages to be logged to a circular
buffer in a flash partition where it can be read back as files after
mounting pstore filesystem.
If unsure, say N.
config MTD_SWAP config MTD_SWAP
tristate "Swap on MTD device support" tristate "Swap on MTD device support"
depends on MTD && SWAP depends on MTD && SWAP
......
...@@ -20,6 +20,7 @@ obj-$(CONFIG_RFD_FTL) += rfd_ftl.o ...@@ -20,6 +20,7 @@ obj-$(CONFIG_RFD_FTL) += rfd_ftl.o
obj-$(CONFIG_SSFDC) += ssfdc.o obj-$(CONFIG_SSFDC) += ssfdc.o
obj-$(CONFIG_SM_FTL) += sm_ftl.o obj-$(CONFIG_SM_FTL) += sm_ftl.o
obj-$(CONFIG_MTD_OOPS) += mtdoops.o obj-$(CONFIG_MTD_OOPS) += mtdoops.o
obj-$(CONFIG_MTD_PSTORE) += mtdpstore.o
obj-$(CONFIG_MTD_SWAP) += mtdswap.o obj-$(CONFIG_MTD_SWAP) += mtdswap.o
nftl-objs := nftlcore.o nftlmount.o nftl-objs := nftlcore.o nftlmount.o
......
This diff is collapsed.
...@@ -57,7 +57,7 @@ static struct ramoops_platform_data chromeos_ramoops_data = { ...@@ -57,7 +57,7 @@ static struct ramoops_platform_data chromeos_ramoops_data = {
.record_size = 0x40000, .record_size = 0x40000,
.console_size = 0x20000, .console_size = 0x20000,
.ftrace_size = 0x20000, .ftrace_size = 0x20000,
.dump_oops = 1, .max_reason = KMSG_DUMP_OOPS,
}; };
static struct platform_device chromeos_ramoops = { static struct platform_device chromeos_ramoops = {
......
...@@ -153,3 +153,112 @@ config PSTORE_RAM ...@@ -153,3 +153,112 @@ config PSTORE_RAM
"ramoops.ko". "ramoops.ko".
For more information, see Documentation/admin-guide/ramoops.rst. For more information, see Documentation/admin-guide/ramoops.rst.
config PSTORE_ZONE
tristate
depends on PSTORE
help
The common layer for pstore/blk (and pstore/ram in the future)
to manage storage in zones.
config PSTORE_BLK
tristate "Log panic/oops to a block device"
depends on PSTORE
depends on BLOCK
select PSTORE_ZONE
default n
help
This enables panic and oops message to be logged to a block dev
where it can be read back at some later point.
For more information, see Documentation/admin-guide/pstore-blk.rst
If unsure, say N.
config PSTORE_BLK_BLKDEV
string "block device identifier"
depends on PSTORE_BLK
default ""
help
Which block device should be used for pstore/blk.
It accepts the following variants:
1) <hex_major><hex_minor> device number in hexadecimal representation,
with no leading 0x, for example b302.
2) /dev/<disk_name> represents the device name of disk
3) /dev/<disk_name><decimal> represents the device name and number
of partition - device number of disk plus the partition number
4) /dev/<disk_name>p<decimal> - same as the above, this form is
used when disk name of partitioned disk ends with a digit.
5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
unique id of a partition if the partition table provides it.
The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
filled hex representation of the 32-bit "NT disk signature", and PP
is a zero-filled hex representation of the 1-based partition number.
6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
to a partition with a known unique id.
7) <major>:<minor> major and minor number of the device separated by
a colon.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_KMSG_SIZE
int "Size in Kbytes of kmsg dump log to store"
depends on PSTORE_BLK
default 64
help
This just sets size of kmsg dump (oops, panic, etc) log for
pstore/blk. The size is in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_MAX_REASON
int "Maximum kmsg dump reason to store"
depends on PSTORE_BLK
default 2
help
The maximum reason for kmsg dumps to store. The default is
2 (KMSG_DUMP_OOPS), see include/linux/kmsg_dump.h's
enum kmsg_dump_reason for more details.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_PMSG_SIZE
int "Size in Kbytes of pmsg to store"
depends on PSTORE_BLK
depends on PSTORE_PMSG
default 64
help
This just sets size of pmsg (pmsg_size) for pstore/blk. The size is
in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_CONSOLE_SIZE
int "Size in Kbytes of console log to store"
depends on PSTORE_BLK
depends on PSTORE_CONSOLE
default 64
help
This just sets size of console log (console_size) to store via
pstore/blk. The size is in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_FTRACE_SIZE
int "Size in Kbytes of ftrace log to store"
depends on PSTORE_BLK
depends on PSTORE_FTRACE
default 64
help
This just sets size of ftrace log (ftrace_size) for pstore/blk. The
size is in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
...@@ -12,3 +12,9 @@ pstore-$(CONFIG_PSTORE_PMSG) += pmsg.o ...@@ -12,3 +12,9 @@ pstore-$(CONFIG_PSTORE_PMSG) += pmsg.o
ramoops-objs += ram.o ram_core.o ramoops-objs += ram.o ram_core.o
obj-$(CONFIG_PSTORE_RAM) += ramoops.o obj-$(CONFIG_PSTORE_RAM) += ramoops.o
pstore_zone-objs += zone.o
obj-$(CONFIG_PSTORE_ZONE) += pstore_zone.o
pstore_blk-objs += blk.o
obj-$(CONFIG_PSTORE_BLK) += pstore_blk.o
This diff is collapsed.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/cache.h> #include <linux/cache.h>
#include <linux/slab.h>
#include <asm/barrier.h> #include <asm/barrier.h>
#include "internal.h" #include "internal.h"
...@@ -132,3 +133,56 @@ void pstore_unregister_ftrace(void) ...@@ -132,3 +133,56 @@ void pstore_unregister_ftrace(void)
debugfs_remove_recursive(pstore_ftrace_dir); debugfs_remove_recursive(pstore_ftrace_dir);
} }
ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
const char *src_log, size_t src_log_size)
{
size_t dest_size, src_size, total, dest_off, src_off;
size_t dest_idx = 0, src_idx = 0, merged_idx = 0;
void *merged_buf;
struct pstore_ftrace_record *drec, *srec, *mrec;
size_t record_size = sizeof(struct pstore_ftrace_record);
dest_off = *dest_log_size % record_size;
dest_size = *dest_log_size - dest_off;
src_off = src_log_size % record_size;
src_size = src_log_size - src_off;
total = dest_size + src_size;
merged_buf = kmalloc(total, GFP_KERNEL);
if (!merged_buf)
return -ENOMEM;
drec = (struct pstore_ftrace_record *)(*dest_log + dest_off);
srec = (struct pstore_ftrace_record *)(src_log + src_off);
mrec = (struct pstore_ftrace_record *)(merged_buf);
while (dest_size > 0 && src_size > 0) {
if (pstore_ftrace_read_timestamp(&drec[dest_idx]) <
pstore_ftrace_read_timestamp(&srec[src_idx])) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
} else {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
}
while (dest_size > 0) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
}
while (src_size > 0) {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
kfree(*dest_log);
*dest_log = merged_buf;
*dest_log_size = total;
return 0;
}
EXPORT_SYMBOL_GPL(pstore_ftrace_combine_log);
...@@ -22,18 +22,21 @@ ...@@ -22,18 +22,21 @@
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/pstore.h> #include <linux/pstore.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "internal.h" #include "internal.h"
#define PSTORE_NAMELEN 64 #define PSTORE_NAMELEN 64
static DEFINE_SPINLOCK(allpstore_lock); static DEFINE_MUTEX(records_list_lock);
static LIST_HEAD(allpstore); static LIST_HEAD(records_list);
static DEFINE_MUTEX(pstore_sb_lock);
static struct super_block *pstore_sb;
struct pstore_private { struct pstore_private {
struct list_head list; struct list_head list;
struct dentry *dentry;
struct pstore_record *record; struct pstore_record *record;
size_t total_size; size_t total_size;
}; };
...@@ -178,10 +181,22 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry) ...@@ -178,10 +181,22 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{ {
struct pstore_private *p = d_inode(dentry)->i_private; struct pstore_private *p = d_inode(dentry)->i_private;
struct pstore_record *record = p->record; struct pstore_record *record = p->record;
int rc = 0;
if (!record->psi->erase) if (!record->psi->erase)
return -EPERM; return -EPERM;
/* Make sure we can't race while removing this file. */
mutex_lock(&records_list_lock);
if (!list_empty(&p->list))
list_del_init(&p->list);
else
rc = -ENOENT;
p->dentry = NULL;
mutex_unlock(&records_list_lock);
if (rc)
return rc;
mutex_lock(&record->psi->read_mutex); mutex_lock(&record->psi->read_mutex);
record->psi->erase(record); record->psi->erase(record);
mutex_unlock(&record->psi->read_mutex); mutex_unlock(&record->psi->read_mutex);
...@@ -192,15 +207,9 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry) ...@@ -192,15 +207,9 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
static void pstore_evict_inode(struct inode *inode) static void pstore_evict_inode(struct inode *inode)
{ {
struct pstore_private *p = inode->i_private; struct pstore_private *p = inode->i_private;
unsigned long flags;
clear_inode(inode); clear_inode(inode);
if (p) {
spin_lock_irqsave(&allpstore_lock, flags);
list_del(&p->list);
spin_unlock_irqrestore(&allpstore_lock, flags);
free_pstore_private(p); free_pstore_private(p);
}
} }
static const struct inode_operations pstore_dir_inode_operations = { static const struct inode_operations pstore_dir_inode_operations = {
...@@ -278,11 +287,54 @@ static const struct super_operations pstore_ops = { ...@@ -278,11 +287,54 @@ static const struct super_operations pstore_ops = {
.show_options = pstore_show_options, .show_options = pstore_show_options,
}; };
static struct super_block *pstore_sb; static struct dentry *psinfo_lock_root(void)
{
struct dentry *root;
bool pstore_is_mounted(void) mutex_lock(&pstore_sb_lock);
/*
* Having no backend is fine -- no records appear.
* Not being mounted is fine -- nothing to do.
*/
if (!psinfo || !pstore_sb) {
mutex_unlock(&pstore_sb_lock);
return NULL;
}
root = pstore_sb->s_root;
inode_lock(d_inode(root));
mutex_unlock(&pstore_sb_lock);
return root;
}
int pstore_put_backend_records(struct pstore_info *psi)
{ {
return pstore_sb != NULL; struct pstore_private *pos, *tmp;
struct dentry *root;
int rc = 0;
root = psinfo_lock_root();
if (!root)
return 0;
mutex_lock(&records_list_lock);
list_for_each_entry_safe(pos, tmp, &records_list, list) {
if (pos->record->psi == psi) {
list_del_init(&pos->list);
rc = simple_unlink(d_inode(root), pos->dentry);
if (WARN_ON(rc))
break;
d_drop(pos->dentry);
dput(pos->dentry);
pos->dentry = NULL;
}
}
mutex_unlock(&records_list_lock);
inode_unlock(d_inode(root));
return rc;
} }
/* /*
...@@ -297,23 +349,20 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record) ...@@ -297,23 +349,20 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
int rc = 0; int rc = 0;
char name[PSTORE_NAMELEN]; char name[PSTORE_NAMELEN];
struct pstore_private *private, *pos; struct pstore_private *private, *pos;
unsigned long flags;
size_t size = record->size + record->ecc_notice_size; size_t size = record->size + record->ecc_notice_size;
WARN_ON(!inode_is_locked(d_inode(root))); if (WARN_ON(!inode_is_locked(d_inode(root))))
return -EINVAL;
spin_lock_irqsave(&allpstore_lock, flags); rc = -EEXIST;
list_for_each_entry(pos, &allpstore, list) { /* Skip records that are already present in the filesystem. */
mutex_lock(&records_list_lock);
list_for_each_entry(pos, &records_list, list) {
if (pos->record->type == record->type && if (pos->record->type == record->type &&
pos->record->id == record->id && pos->record->id == record->id &&
pos->record->psi == record->psi) { pos->record->psi == record->psi)
rc = -EEXIST; goto fail;
break;
}
} }
spin_unlock_irqrestore(&allpstore_lock, flags);
if (rc)
return rc;
rc = -ENOMEM; rc = -ENOMEM;
inode = pstore_get_inode(root->d_sb); inode = pstore_get_inode(root->d_sb);
...@@ -334,6 +383,7 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record) ...@@ -334,6 +383,7 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
if (!dentry) if (!dentry)
goto fail_private; goto fail_private;
private->dentry = dentry;
private->record = record; private->record = record;
inode->i_size = private->total_size = size; inode->i_size = private->total_size = size;
inode->i_private = private; inode->i_private = private;
...@@ -343,9 +393,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record) ...@@ -343,9 +393,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
d_add(dentry, inode); d_add(dentry, inode);
spin_lock_irqsave(&allpstore_lock, flags); list_add(&private->list, &records_list);
list_add(&private->list, &allpstore); mutex_unlock(&records_list_lock);
spin_unlock_irqrestore(&allpstore_lock, flags);
return 0; return 0;
...@@ -353,8 +402,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record) ...@@ -353,8 +402,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
free_pstore_private(private); free_pstore_private(private);
fail_inode: fail_inode:
iput(inode); iput(inode);
fail: fail:
mutex_unlock(&records_list_lock);
return rc; return rc;
} }
...@@ -366,16 +415,13 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record) ...@@ -366,16 +415,13 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
*/ */
void pstore_get_records(int quiet) void pstore_get_records(int quiet)
{ {
struct pstore_info *psi = psinfo;
struct dentry *root; struct dentry *root;
if (!psi || !pstore_sb) root = psinfo_lock_root();
if (!root)
return; return;
root = pstore_sb->s_root; pstore_get_backend_records(psinfo, root, quiet);
inode_lock(d_inode(root));
pstore_get_backend_records(psi, root, quiet);
inode_unlock(d_inode(root)); inode_unlock(d_inode(root));
} }
...@@ -383,8 +429,6 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent) ...@@ -383,8 +429,6 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct inode *inode; struct inode *inode;
pstore_sb = sb;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_SIZE; sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;
...@@ -405,6 +449,10 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent) ...@@ -405,6 +449,10 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
if (!sb->s_root) if (!sb->s_root)
return -ENOMEM; return -ENOMEM;
mutex_lock(&pstore_sb_lock);
pstore_sb = sb;
mutex_unlock(&pstore_sb_lock);
pstore_get_records(0); pstore_get_records(0);
return 0; return 0;
...@@ -418,8 +466,17 @@ static struct dentry *pstore_mount(struct file_system_type *fs_type, ...@@ -418,8 +466,17 @@ static struct dentry *pstore_mount(struct file_system_type *fs_type,
static void pstore_kill_sb(struct super_block *sb) static void pstore_kill_sb(struct super_block *sb)
{ {
mutex_lock(&pstore_sb_lock);
WARN_ON(pstore_sb != sb);
kill_litter_super(sb); kill_litter_super(sb);
pstore_sb = NULL; pstore_sb = NULL;
mutex_lock(&records_list_lock);
INIT_LIST_HEAD(&records_list);
mutex_unlock(&records_list_lock);
mutex_unlock(&pstore_sb_lock);
} }
static struct file_system_type pstore_fs_type = { static struct file_system_type pstore_fs_type = {
......
...@@ -12,9 +12,18 @@ extern unsigned long kmsg_bytes; ...@@ -12,9 +12,18 @@ extern unsigned long kmsg_bytes;
#ifdef CONFIG_PSTORE_FTRACE #ifdef CONFIG_PSTORE_FTRACE
extern void pstore_register_ftrace(void); extern void pstore_register_ftrace(void);
extern void pstore_unregister_ftrace(void); extern void pstore_unregister_ftrace(void);
ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
const char *src_log, size_t src_log_size);
#else #else
static inline void pstore_register_ftrace(void) {} static inline void pstore_register_ftrace(void) {}
static inline void pstore_unregister_ftrace(void) {} static inline void pstore_unregister_ftrace(void) {}
static inline ssize_t
pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
const char *src_log, size_t src_log_size)
{
*dest_log_size = 0;
return 0;
}
#endif #endif
#ifdef CONFIG_PSTORE_PMSG #ifdef CONFIG_PSTORE_PMSG
...@@ -31,9 +40,9 @@ extern void pstore_set_kmsg_bytes(int); ...@@ -31,9 +40,9 @@ extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(int); extern void pstore_get_records(int);
extern void pstore_get_backend_records(struct pstore_info *psi, extern void pstore_get_backend_records(struct pstore_info *psi,
struct dentry *root, int quiet); struct dentry *root, int quiet);
extern int pstore_put_backend_records(struct pstore_info *psi);
extern int pstore_mkfile(struct dentry *root, extern int pstore_mkfile(struct dentry *root,
struct pstore_record *record); struct pstore_record *record);
extern bool pstore_is_mounted(void);
extern void pstore_record_init(struct pstore_record *record, extern void pstore_record_init(struct pstore_record *record,
struct pstore_info *psi); struct pstore_info *psi);
......
...@@ -44,7 +44,7 @@ static int pstore_update_ms = -1; ...@@ -44,7 +44,7 @@ static int pstore_update_ms = -1;
module_param_named(update_ms, pstore_update_ms, int, 0600); module_param_named(update_ms, pstore_update_ms, int, 0600);
MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content " MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content "
"(default is -1, which means runtime updates are disabled; " "(default is -1, which means runtime updates are disabled; "
"enabling this option is not safe, it may lead to further " "enabling this option may not be safe; it may lead to further "
"corruption on Oopses)"); "corruption on Oopses)");
/* Names should be in the same order as the enum pstore_type_id */ /* Names should be in the same order as the enum pstore_type_id */
...@@ -69,19 +69,25 @@ static void pstore_dowork(struct work_struct *); ...@@ -69,19 +69,25 @@ static void pstore_dowork(struct work_struct *);
static DECLARE_WORK(pstore_work, pstore_dowork); static DECLARE_WORK(pstore_work, pstore_dowork);
/* /*
* pstore_lock just protects "psinfo" during * psinfo_lock protects "psinfo" during calls to
* calls to pstore_register() * pstore_register(), pstore_unregister(), and
* the filesystem mount/unmount routines.
*/ */
static DEFINE_SPINLOCK(pstore_lock); static DEFINE_MUTEX(psinfo_lock);
struct pstore_info *psinfo; struct pstore_info *psinfo;
static char *backend; static char *backend;
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "specific backend to use");
static char *compress = static char *compress =
#ifdef CONFIG_PSTORE_COMPRESS_DEFAULT #ifdef CONFIG_PSTORE_COMPRESS_DEFAULT
CONFIG_PSTORE_COMPRESS_DEFAULT; CONFIG_PSTORE_COMPRESS_DEFAULT;
#else #else
NULL; NULL;
#endif #endif
module_param(compress, charp, 0444);
MODULE_PARM_DESC(compress, "compression to use");
/* Compression parameters */ /* Compression parameters */
static struct crypto_comp *tfm; static struct crypto_comp *tfm;
...@@ -129,24 +135,12 @@ enum pstore_type_id pstore_name_to_type(const char *name) ...@@ -129,24 +135,12 @@ enum pstore_type_id pstore_name_to_type(const char *name)
} }
EXPORT_SYMBOL_GPL(pstore_name_to_type); EXPORT_SYMBOL_GPL(pstore_name_to_type);
static const char *get_reason_str(enum kmsg_dump_reason reason) static void pstore_timer_kick(void)
{ {
switch (reason) { if (pstore_update_ms < 0)
case KMSG_DUMP_PANIC: return;
return "Panic";
case KMSG_DUMP_OOPS: mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
return "Oops";
case KMSG_DUMP_EMERG:
return "Emergency";
case KMSG_DUMP_RESTART:
return "Restart";
case KMSG_DUMP_HALT:
return "Halt";
case KMSG_DUMP_POWEROFF:
return "Poweroff";
default:
return "Unknown";
}
} }
/* /*
...@@ -393,7 +387,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -393,7 +387,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
unsigned int part = 1; unsigned int part = 1;
int ret; int ret;
why = get_reason_str(reason); why = kmsg_dump_reason_str(reason);
if (down_trylock(&psinfo->buf_lock)) { if (down_trylock(&psinfo->buf_lock)) {
/* Failed to acquire lock: give up if we cannot wait. */ /* Failed to acquire lock: give up if we cannot wait. */
...@@ -459,8 +453,10 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -459,8 +453,10 @@ static void pstore_dump(struct kmsg_dumper *dumper,
} }
ret = psinfo->write(&record); ret = psinfo->write(&record);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) if (ret == 0 && reason == KMSG_DUMP_OOPS) {
pstore_new_entry = 1; pstore_new_entry = 1;
pstore_timer_kick();
}
total += record.size; total += record.size;
part++; part++;
...@@ -503,14 +499,20 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) ...@@ -503,14 +499,20 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
} }
static struct console pstore_console = { static struct console pstore_console = {
.name = "pstore",
.write = pstore_console_write, .write = pstore_console_write,
.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME,
.index = -1, .index = -1,
}; };
static void pstore_register_console(void) static void pstore_register_console(void)
{ {
/* Show which backend is going to get console writes. */
strscpy(pstore_console.name, psinfo->name,
sizeof(pstore_console.name));
/*
* Always initialize flags here since prior unregister_console()
* calls may have changed settings (specifically CON_ENABLED).
*/
pstore_console.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME;
register_console(&pstore_console); register_console(&pstore_console);
} }
...@@ -555,8 +557,6 @@ static int pstore_write_user_compat(struct pstore_record *record, ...@@ -555,8 +557,6 @@ static int pstore_write_user_compat(struct pstore_record *record,
*/ */
int pstore_register(struct pstore_info *psi) int pstore_register(struct pstore_info *psi)
{ {
struct module *owner = psi->owner;
if (backend && strcmp(backend, psi->name)) { if (backend && strcmp(backend, psi->name)) {
pr_warn("ignoring unexpected backend '%s'\n", psi->name); pr_warn("ignoring unexpected backend '%s'\n", psi->name);
return -EPERM; return -EPERM;
...@@ -576,11 +576,11 @@ int pstore_register(struct pstore_info *psi) ...@@ -576,11 +576,11 @@ int pstore_register(struct pstore_info *psi)
return -EINVAL; return -EINVAL;
} }
spin_lock(&pstore_lock); mutex_lock(&psinfo_lock);
if (psinfo) { if (psinfo) {
pr_warn("backend '%s' already loaded: ignoring '%s'\n", pr_warn("backend '%s' already loaded: ignoring '%s'\n",
psinfo->name, psi->name); psinfo->name, psi->name);
spin_unlock(&pstore_lock); mutex_unlock(&psinfo_lock);
return -EBUSY; return -EBUSY;
} }
...@@ -589,21 +589,16 @@ int pstore_register(struct pstore_info *psi) ...@@ -589,21 +589,16 @@ int pstore_register(struct pstore_info *psi)
psinfo = psi; psinfo = psi;
mutex_init(&psinfo->read_mutex); mutex_init(&psinfo->read_mutex);
sema_init(&psinfo->buf_lock, 1); sema_init(&psinfo->buf_lock, 1);
spin_unlock(&pstore_lock);
if (owner && !try_module_get(owner)) {
psinfo = NULL;
return -EINVAL;
}
if (psi->flags & PSTORE_FLAGS_DMESG) if (psi->flags & PSTORE_FLAGS_DMESG)
allocate_buf_for_compression(); allocate_buf_for_compression();
if (pstore_is_mounted())
pstore_get_records(0); pstore_get_records(0);
if (psi->flags & PSTORE_FLAGS_DMESG) if (psi->flags & PSTORE_FLAGS_DMESG) {
pstore_dumper.max_reason = psinfo->max_reason;
pstore_register_kmsg(); pstore_register_kmsg();
}
if (psi->flags & PSTORE_FLAGS_CONSOLE) if (psi->flags & PSTORE_FLAGS_CONSOLE)
pstore_register_console(); pstore_register_console();
if (psi->flags & PSTORE_FLAGS_FTRACE) if (psi->flags & PSTORE_FLAGS_FTRACE)
...@@ -612,33 +607,36 @@ int pstore_register(struct pstore_info *psi) ...@@ -612,33 +607,36 @@ int pstore_register(struct pstore_info *psi)
pstore_register_pmsg(); pstore_register_pmsg();
/* Start watching for new records, if desired. */ /* Start watching for new records, if desired. */
if (pstore_update_ms >= 0) { pstore_timer_kick();
pstore_timer.expires = jiffies +
msecs_to_jiffies(pstore_update_ms);
add_timer(&pstore_timer);
}
/* /*
* Update the module parameter backend, so it is visible * Update the module parameter backend, so it is visible
* through /sys/module/pstore/parameters/backend * through /sys/module/pstore/parameters/backend
*/ */
backend = psi->name; backend = kstrdup(psi->name, GFP_KERNEL);
pr_info("Registered %s as persistent store backend\n", psi->name); pr_info("Registered %s as persistent store backend\n", psi->name);
module_put(owner); mutex_unlock(&psinfo_lock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(pstore_register); EXPORT_SYMBOL_GPL(pstore_register);
void pstore_unregister(struct pstore_info *psi) void pstore_unregister(struct pstore_info *psi)
{ {
/* Stop timer and make sure all work has finished. */ /* It's okay to unregister nothing. */
pstore_update_ms = -1; if (!psi)
del_timer_sync(&pstore_timer); return;
flush_work(&pstore_work);
mutex_lock(&psinfo_lock);
/* Only one backend can be registered at a time. */
if (WARN_ON(psi != psinfo)) {
mutex_unlock(&psinfo_lock);
return;
}
/* Unregister all callbacks. */
if (psi->flags & PSTORE_FLAGS_PMSG) if (psi->flags & PSTORE_FLAGS_PMSG)
pstore_unregister_pmsg(); pstore_unregister_pmsg();
if (psi->flags & PSTORE_FLAGS_FTRACE) if (psi->flags & PSTORE_FLAGS_FTRACE)
...@@ -648,10 +646,19 @@ void pstore_unregister(struct pstore_info *psi) ...@@ -648,10 +646,19 @@ void pstore_unregister(struct pstore_info *psi)
if (psi->flags & PSTORE_FLAGS_DMESG) if (psi->flags & PSTORE_FLAGS_DMESG)
pstore_unregister_kmsg(); pstore_unregister_kmsg();
/* Stop timer and make sure all work has finished. */
del_timer_sync(&pstore_timer);
flush_work(&pstore_work);
/* Remove all backend records from filesystem tree. */
pstore_put_backend_records(psi);
free_buf_for_compression(); free_buf_for_compression();
psinfo = NULL; psinfo = NULL;
kfree(backend);
backend = NULL; backend = NULL;
mutex_unlock(&psinfo_lock);
} }
EXPORT_SYMBOL_GPL(pstore_unregister); EXPORT_SYMBOL_GPL(pstore_unregister);
...@@ -788,9 +795,7 @@ static void pstore_timefunc(struct timer_list *unused) ...@@ -788,9 +795,7 @@ static void pstore_timefunc(struct timer_list *unused)
schedule_work(&pstore_work); schedule_work(&pstore_work);
} }
if (pstore_update_ms >= 0) pstore_timer_kick();
mod_timer(&pstore_timer,
jiffies + msecs_to_jiffies(pstore_update_ms));
} }
static void __init pstore_choose_compression(void) static void __init pstore_choose_compression(void)
...@@ -835,11 +840,5 @@ static void __exit pstore_exit(void) ...@@ -835,11 +840,5 @@ static void __exit pstore_exit(void)
} }
module_exit(pstore_exit) module_exit(pstore_exit)
module_param(compress, charp, 0444);
MODULE_PARM_DESC(compress, "Pstore compression to use");
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "Pstore backend to use");
MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>"); MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/pstore_ram.h> #include <linux/pstore_ram.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include "internal.h"
#define RAMOOPS_KERNMSG_HDR "====" #define RAMOOPS_KERNMSG_HDR "===="
#define MIN_MEM_SIZE 4096UL #define MIN_MEM_SIZE 4096UL
...@@ -53,22 +54,27 @@ MODULE_PARM_DESC(mem_size, ...@@ -53,22 +54,27 @@ MODULE_PARM_DESC(mem_size,
"size of reserved RAM used to store oops/panic logs"); "size of reserved RAM used to store oops/panic logs");
static unsigned int mem_type; static unsigned int mem_type;
module_param(mem_type, uint, 0600); module_param(mem_type, uint, 0400);
MODULE_PARM_DESC(mem_type, MODULE_PARM_DESC(mem_type,
"set to 1 to try to use unbuffered memory (default 0)"); "set to 1 to try to use unbuffered memory (default 0)");
static int dump_oops = 1; static int ramoops_max_reason = -1;
module_param(dump_oops, int, 0600); module_param_named(max_reason, ramoops_max_reason, int, 0400);
MODULE_PARM_DESC(dump_oops, MODULE_PARM_DESC(max_reason,
"set to 1 to dump oopses, 0 to only dump panics (default 1)"); "maximum reason for kmsg dump (default 2: Oops and Panic) ");
static int ramoops_ecc; static int ramoops_ecc;
module_param_named(ecc, ramoops_ecc, int, 0600); module_param_named(ecc, ramoops_ecc, int, 0400);
MODULE_PARM_DESC(ramoops_ecc, MODULE_PARM_DESC(ramoops_ecc,
"if non-zero, the option enables ECC support and specifies " "if non-zero, the option enables ECC support and specifies "
"ECC buffer size in bytes (1 is a special value, means 16 " "ECC buffer size in bytes (1 is a special value, means 16 "
"bytes ECC)"); "bytes ECC)");
static int ramoops_dump_oops = -1;
module_param_named(dump_oops, ramoops_dump_oops, int, 0400);
MODULE_PARM_DESC(dump_oops,
"(deprecated: use max_reason instead) set to 1 to dump oopses & panics, 0 to only dump panics");
struct ramoops_context { struct ramoops_context {
struct persistent_ram_zone **dprzs; /* Oops dump zones */ struct persistent_ram_zone **dprzs; /* Oops dump zones */
struct persistent_ram_zone *cprz; /* Console zone */ struct persistent_ram_zone *cprz; /* Console zone */
...@@ -81,7 +87,6 @@ struct ramoops_context { ...@@ -81,7 +87,6 @@ struct ramoops_context {
size_t console_size; size_t console_size;
size_t ftrace_size; size_t ftrace_size;
size_t pmsg_size; size_t pmsg_size;
int dump_oops;
u32 flags; u32 flags;
struct persistent_ram_ecc_info ecc_info; struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt; unsigned int max_dump_cnt;
...@@ -168,58 +173,6 @@ static bool prz_ok(struct persistent_ram_zone *prz) ...@@ -168,58 +173,6 @@ static bool prz_ok(struct persistent_ram_zone *prz)
persistent_ram_ecc_string(prz, NULL, 0)); persistent_ram_ecc_string(prz, NULL, 0));
} }
static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest,
struct persistent_ram_zone *src)
{
size_t dest_size, src_size, total, dest_off, src_off;
size_t dest_idx = 0, src_idx = 0, merged_idx = 0;
void *merged_buf;
struct pstore_ftrace_record *drec, *srec, *mrec;
size_t record_size = sizeof(struct pstore_ftrace_record);
dest_off = dest->old_log_size % record_size;
dest_size = dest->old_log_size - dest_off;
src_off = src->old_log_size % record_size;
src_size = src->old_log_size - src_off;
total = dest_size + src_size;
merged_buf = kmalloc(total, GFP_KERNEL);
if (!merged_buf)
return -ENOMEM;
drec = (struct pstore_ftrace_record *)(dest->old_log + dest_off);
srec = (struct pstore_ftrace_record *)(src->old_log + src_off);
mrec = (struct pstore_ftrace_record *)(merged_buf);
while (dest_size > 0 && src_size > 0) {
if (pstore_ftrace_read_timestamp(&drec[dest_idx]) <
pstore_ftrace_read_timestamp(&srec[src_idx])) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
} else {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
}
while (dest_size > 0) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
}
while (src_size > 0) {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
kfree(dest->old_log);
dest->old_log = merged_buf;
dest->old_log_size = total;
return 0;
}
static ssize_t ramoops_pstore_read(struct pstore_record *record) static ssize_t ramoops_pstore_read(struct pstore_record *record)
{ {
ssize_t size = 0; ssize_t size = 0;
...@@ -291,7 +244,12 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record) ...@@ -291,7 +244,12 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
tmp_prz->corrected_bytes += tmp_prz->corrected_bytes +=
prz_next->corrected_bytes; prz_next->corrected_bytes;
tmp_prz->bad_blocks += prz_next->bad_blocks; tmp_prz->bad_blocks += prz_next->bad_blocks;
size = ftrace_log_combine(tmp_prz, prz_next);
size = pstore_ftrace_combine_log(
&tmp_prz->old_log,
&tmp_prz->old_log_size,
prz_next->old_log,
prz_next->old_log_size);
if (size) if (size)
goto out; goto out;
} }
...@@ -382,16 +340,14 @@ static int notrace ramoops_pstore_write(struct pstore_record *record) ...@@ -382,16 +340,14 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
return -EINVAL; return -EINVAL;
/* /*
* Out of the various dmesg dump types, ramoops is currently designed * We could filter on record->reason here if we wanted to (which
* to only store crash logs, rather than storing general kernel logs. * would duplicate what happened before the "max_reason" setting
* was added), but that would defeat the purpose of a system
* changing printk.always_kmsg_dump, so instead log everything that
* the kmsg dumper sends us, since it should be doing the filtering
* based on the combination of printk.always_kmsg_dump and our
* requested "max_reason".
*/ */
if (record->reason != KMSG_DUMP_OOPS &&
record->reason != KMSG_DUMP_PANIC)
return -EINVAL;
/* Skip Oopes when configured to do so. */
if (record->reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
return -EINVAL;
/* /*
* Explicitly only take the first part of any new crash. * Explicitly only take the first part of any new crash.
...@@ -644,19 +600,25 @@ static int ramoops_init_prz(const char *name, ...@@ -644,19 +600,25 @@ static int ramoops_init_prz(const char *name,
return 0; return 0;
} }
static int ramoops_parse_dt_size(struct platform_device *pdev, /* Read a u32 from a dt property and make sure it's safe for an int. */
const char *propname, u32 *value) static int ramoops_parse_dt_u32(struct platform_device *pdev,
const char *propname,
u32 default_value, u32 *value)
{ {
u32 val32 = 0; u32 val32 = 0;
int ret; int ret;
ret = of_property_read_u32(pdev->dev.of_node, propname, &val32); ret = of_property_read_u32(pdev->dev.of_node, propname, &val32);
if (ret < 0 && ret != -EINVAL) { if (ret == -EINVAL) {
/* field is missing, use default value. */
val32 = default_value;
} else if (ret < 0) {
dev_err(&pdev->dev, "failed to parse property %s: %d\n", dev_err(&pdev->dev, "failed to parse property %s: %d\n",
propname, ret); propname, ret);
return ret; return ret;
} }
/* Sanity check our results. */
if (val32 > INT_MAX) { if (val32 > INT_MAX) {
dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32); dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32);
return -EOVERFLOW; return -EOVERFLOW;
...@@ -687,23 +649,32 @@ static int ramoops_parse_dt(struct platform_device *pdev, ...@@ -687,23 +649,32 @@ static int ramoops_parse_dt(struct platform_device *pdev,
pdata->mem_size = resource_size(res); pdata->mem_size = resource_size(res);
pdata->mem_address = res->start; pdata->mem_address = res->start;
pdata->mem_type = of_property_read_bool(of_node, "unbuffered"); pdata->mem_type = of_property_read_bool(of_node, "unbuffered");
pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops"); /*
* Setting "no-dump-oops" is deprecated and will be ignored if
* "max_reason" is also specified.
*/
if (of_property_read_bool(of_node, "no-dump-oops"))
pdata->max_reason = KMSG_DUMP_PANIC;
else
pdata->max_reason = KMSG_DUMP_OOPS;
#define parse_size(name, field) { \ #define parse_u32(name, field, default_value) { \
ret = ramoops_parse_dt_size(pdev, name, &value); \ ret = ramoops_parse_dt_u32(pdev, name, default_value, \
&value); \
if (ret < 0) \ if (ret < 0) \
return ret; \ return ret; \
field = value; \ field = value; \
} }
parse_size("record-size", pdata->record_size); parse_u32("record-size", pdata->record_size, 0);
parse_size("console-size", pdata->console_size); parse_u32("console-size", pdata->console_size, 0);
parse_size("ftrace-size", pdata->ftrace_size); parse_u32("ftrace-size", pdata->ftrace_size, 0);
parse_size("pmsg-size", pdata->pmsg_size); parse_u32("pmsg-size", pdata->pmsg_size, 0);
parse_size("ecc-size", pdata->ecc_info.ecc_size); parse_u32("ecc-size", pdata->ecc_info.ecc_size, 0);
parse_size("flags", pdata->flags); parse_u32("flags", pdata->flags, 0);
parse_u32("max-reason", pdata->max_reason, pdata->max_reason);
#undef parse_size #undef parse_u32
/* /*
* Some old Chromebooks relied on the kernel setting the * Some old Chromebooks relied on the kernel setting the
...@@ -785,7 +756,6 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -785,7 +756,6 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->console_size = pdata->console_size; cxt->console_size = pdata->console_size;
cxt->ftrace_size = pdata->ftrace_size; cxt->ftrace_size = pdata->ftrace_size;
cxt->pmsg_size = pdata->pmsg_size; cxt->pmsg_size = pdata->pmsg_size;
cxt->dump_oops = pdata->dump_oops;
cxt->flags = pdata->flags; cxt->flags = pdata->flags;
cxt->ecc_info = pdata->ecc_info; cxt->ecc_info = pdata->ecc_info;
...@@ -828,8 +798,10 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -828,8 +798,10 @@ static int ramoops_probe(struct platform_device *pdev)
* the single region size is how to check. * the single region size is how to check.
*/ */
cxt->pstore.flags = 0; cxt->pstore.flags = 0;
if (cxt->max_dump_cnt) if (cxt->max_dump_cnt) {
cxt->pstore.flags |= PSTORE_FLAGS_DMESG; cxt->pstore.flags |= PSTORE_FLAGS_DMESG;
cxt->pstore.max_reason = pdata->max_reason;
}
if (cxt->console_size) if (cxt->console_size)
cxt->pstore.flags |= PSTORE_FLAGS_CONSOLE; cxt->pstore.flags |= PSTORE_FLAGS_CONSOLE;
if (cxt->max_ftrace_cnt) if (cxt->max_ftrace_cnt)
...@@ -865,7 +837,7 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -865,7 +837,7 @@ static int ramoops_probe(struct platform_device *pdev)
mem_size = pdata->mem_size; mem_size = pdata->mem_size;
mem_address = pdata->mem_address; mem_address = pdata->mem_address;
record_size = pdata->record_size; record_size = pdata->record_size;
dump_oops = pdata->dump_oops; ramoops_max_reason = pdata->max_reason;
ramoops_console_size = pdata->console_size; ramoops_console_size = pdata->console_size;
ramoops_pmsg_size = pdata->pmsg_size; ramoops_pmsg_size = pdata->pmsg_size;
ramoops_ftrace_size = pdata->ftrace_size; ramoops_ftrace_size = pdata->ftrace_size;
...@@ -948,7 +920,16 @@ static void __init ramoops_register_dummy(void) ...@@ -948,7 +920,16 @@ static void __init ramoops_register_dummy(void)
pdata.console_size = ramoops_console_size; pdata.console_size = ramoops_console_size;
pdata.ftrace_size = ramoops_ftrace_size; pdata.ftrace_size = ramoops_ftrace_size;
pdata.pmsg_size = ramoops_pmsg_size; pdata.pmsg_size = ramoops_pmsg_size;
pdata.dump_oops = dump_oops; /* If "max_reason" is set, its value has priority over "dump_oops". */
if (ramoops_max_reason >= 0)
pdata.max_reason = ramoops_max_reason;
/* Otherwise, if "dump_oops" is set, parse it into "max_reason". */
else if (ramoops_dump_oops != -1)
pdata.max_reason = ramoops_dump_oops ? KMSG_DUMP_OOPS
: KMSG_DUMP_PANIC;
/* And if neither are explicitly set, use the default. */
else
pdata.max_reason = KMSG_DUMP_OOPS;
pdata.flags = RAMOOPS_FLAG_FTRACE_PER_CPU; pdata.flags = RAMOOPS_FLAG_FTRACE_PER_CPU;
/* /*
......
This diff is collapsed.
...@@ -25,9 +25,8 @@ enum kmsg_dump_reason { ...@@ -25,9 +25,8 @@ enum kmsg_dump_reason {
KMSG_DUMP_PANIC, KMSG_DUMP_PANIC,
KMSG_DUMP_OOPS, KMSG_DUMP_OOPS,
KMSG_DUMP_EMERG, KMSG_DUMP_EMERG,
KMSG_DUMP_RESTART, KMSG_DUMP_SHUTDOWN,
KMSG_DUMP_HALT, KMSG_DUMP_MAX
KMSG_DUMP_POWEROFF,
}; };
/** /**
...@@ -71,6 +70,8 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper); ...@@ -71,6 +70,8 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper);
int kmsg_dump_register(struct kmsg_dumper *dumper); int kmsg_dump_register(struct kmsg_dumper *dumper);
int kmsg_dump_unregister(struct kmsg_dumper *dumper); int kmsg_dump_unregister(struct kmsg_dumper *dumper);
const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason);
#else #else
static inline void kmsg_dump(enum kmsg_dump_reason reason) static inline void kmsg_dump(enum kmsg_dump_reason reason)
{ {
...@@ -112,6 +113,11 @@ static inline int kmsg_dump_unregister(struct kmsg_dumper *dumper) ...@@ -112,6 +113,11 @@ static inline int kmsg_dump_unregister(struct kmsg_dumper *dumper)
{ {
return -EINVAL; return -EINVAL;
} }
static inline const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason)
{
return "Disabled";
}
#endif #endif
#endif /* _LINUX_KMSG_DUMP_H */ #endif /* _LINUX_KMSG_DUMP_H */
...@@ -96,6 +96,12 @@ struct pstore_record { ...@@ -96,6 +96,12 @@ struct pstore_record {
* *
* @read_mutex: serializes @open, @read, @close, and @erase callbacks * @read_mutex: serializes @open, @read, @close, and @erase callbacks
* @flags: bitfield of frontends the backend can accept writes for * @flags: bitfield of frontends the backend can accept writes for
* @max_reason: Used when PSTORE_FLAGS_DMESG is set. Contains the
* kmsg_dump_reason enum value. KMSG_DUMP_UNDEF means
* "use existing kmsg_dump() filtering, based on the
* printk.always_kmsg_dump boot param" (which is either
* KMSG_DUMP_OOPS when false, or KMSG_DUMP_MAX when
* true); see printk.always_kmsg_dump for more details.
* @data: backend-private pointer passed back during callbacks * @data: backend-private pointer passed back during callbacks
* *
* Callbacks: * Callbacks:
...@@ -170,7 +176,7 @@ struct pstore_record { ...@@ -170,7 +176,7 @@ struct pstore_record {
*/ */
struct pstore_info { struct pstore_info {
struct module *owner; struct module *owner;
char *name; const char *name;
struct semaphore buf_lock; struct semaphore buf_lock;
char *buf; char *buf;
...@@ -179,6 +185,7 @@ struct pstore_info { ...@@ -179,6 +185,7 @@ struct pstore_info {
struct mutex read_mutex; struct mutex read_mutex;
int flags; int flags;
int max_reason;
void *data; void *data;
int (*open)(struct pstore_info *psi); int (*open)(struct pstore_info *psi);
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PSTORE_BLK_H_
#define __PSTORE_BLK_H_
#include <linux/types.h>
#include <linux/pstore.h>
#include <linux/pstore_zone.h>
/**
* typedef pstore_blk_panic_write_op - panic write operation to block device
*
* @buf: the data to write
* @start_sect: start sector to block device
* @sects: sectors count on buf
*
* Return: On success, zero should be returned. Others excluding -ENOMSG
* mean error. -ENOMSG means to try next zone.
*
* Panic write to block device must be aligned to SECTOR_SIZE.
*/
typedef int (*pstore_blk_panic_write_op)(const char *buf, sector_t start_sect,
sector_t sects);
/**
* struct pstore_blk_info - pstore/blk registration details
*
* @major: Which major device number to support with pstore/blk
* @flags: The supported PSTORE_FLAGS_* from linux/pstore.h.
* @panic_write:The write operation only used for the panic case.
* This can be NULL, but is recommended to avoid losing
* crash data if the kernel's IO path or work queues are
* broken during a panic.
* @devt: The dev_t that pstore/blk has attached to.
* @nr_sects: Number of sectors on @devt.
* @start_sect: Starting sector on @devt.
*/
struct pstore_blk_info {
unsigned int major;
unsigned int flags;
pstore_blk_panic_write_op panic_write;
/* Filled in by pstore/blk after registration. */
dev_t devt;
sector_t nr_sects;
sector_t start_sect;
};
int register_pstore_blk(struct pstore_blk_info *info);
void unregister_pstore_blk(unsigned int major);
/**
* struct pstore_device_info - back-end pstore/blk driver structure.
*
* @total_size: The total size in bytes pstore/blk can use. It must be greater
* than 4096 and be multiple of 4096.
* @flags: Refer to macro starting with PSTORE_FLAGS defined in
* linux/pstore.h. It means what front-ends this device support.
* Zero means all backends for compatible.
* @read: The general read operation. Both of the function parameters
* @size and @offset are relative value to bock device (not the
* whole disk).
* On success, the number of bytes should be returned, others
* means error.
* @write: The same as @read, but the following error number:
* -EBUSY means try to write again later.
* -ENOMSG means to try next zone.
* @erase: The general erase operation for device with special removing
* job. Both of the function parameters @size and @offset are
* relative value to storage.
* Return 0 on success and others on failure.
* @panic_write:The write operation only used for panic case. It's optional
* if you do not care panic log. The parameters are relative
* value to storage.
* On success, the number of bytes should be returned, others
* excluding -ENOMSG mean error. -ENOMSG means to try next zone.
*/
struct pstore_device_info {
unsigned long total_size;
unsigned int flags;
pstore_zone_read_op read;
pstore_zone_write_op write;
pstore_zone_erase_op erase;
pstore_zone_write_op panic_write;
};
int register_pstore_device(struct pstore_device_info *dev);
void unregister_pstore_device(struct pstore_device_info *dev);
/**
* struct pstore_blk_config - the pstore_blk backend configuration
*
* @device: Name of the desired block device
* @max_reason: Maximum kmsg dump reason to store to block device
* @kmsg_size: Total size of for kmsg dumps
* @pmsg_size: Total size of the pmsg storage area
* @console_size: Total size of the console storage area
* @ftrace_size: Total size for ftrace logging data (for all CPUs)
*/
struct pstore_blk_config {
char device[80];
enum kmsg_dump_reason max_reason;
unsigned long kmsg_size;
unsigned long pmsg_size;
unsigned long console_size;
unsigned long ftrace_size;
};
/**
* pstore_blk_get_config - get a copy of the pstore_blk backend configuration
*
* @info: The sturct pstore_blk_config to be filled in
*
* Failure returns negative error code, and success returns 0.
*/
int pstore_blk_get_config(struct pstore_blk_config *info);
#endif
...@@ -133,7 +133,7 @@ struct ramoops_platform_data { ...@@ -133,7 +133,7 @@ struct ramoops_platform_data {
unsigned long console_size; unsigned long console_size;
unsigned long ftrace_size; unsigned long ftrace_size;
unsigned long pmsg_size; unsigned long pmsg_size;
int dump_oops; int max_reason;
u32 flags; u32 flags;
struct persistent_ram_ecc_info ecc_info; struct persistent_ram_ecc_info ecc_info;
}; };
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PSTORE_ZONE_H_
#define __PSTORE_ZONE_H_
#include <linux/types.h>
typedef ssize_t (*pstore_zone_read_op)(char *, size_t, loff_t);
typedef ssize_t (*pstore_zone_write_op)(const char *, size_t, loff_t);
typedef ssize_t (*pstore_zone_erase_op)(size_t, loff_t);
/**
* struct pstore_zone_info - pstore/zone back-end driver structure
*
* @owner: Module which is responsible for this back-end driver.
* @name: Name of the back-end driver.
* @total_size: The total size in bytes pstore/zone can use. It must be greater
* than 4096 and be multiple of 4096.
* @kmsg_size: The size of oops/panic zone. Zero means disabled, otherwise,
* it must be multiple of SECTOR_SIZE(512 Bytes).
* @max_reason: Maximum kmsg dump reason to store.
* @pmsg_size: The size of pmsg zone which is the same as @kmsg_size.
* @console_size:The size of console zone which is the same as @kmsg_size.
* @ftrace_size:The size of ftrace zone which is the same as @kmsg_size.
* @read: The general read operation. Both of the function parameters
* @size and @offset are relative value to storage.
* On success, the number of bytes should be returned, others
* mean error.
* @write: The same as @read, but the following error number:
* -EBUSY means try to write again later.
* -ENOMSG means to try next zone.
* @erase: The general erase operation for device with special removing
* job. Both of the function parameters @size and @offset are
* relative value to storage.
* Return 0 on success and others on failure.
* @panic_write:The write operation only used for panic case. It's optional
* if you do not care panic log. The parameters are relative
* value to storage.
* On success, the number of bytes should be returned, others
* excluding -ENOMSG mean error. -ENOMSG means to try next zone.
*/
struct pstore_zone_info {
struct module *owner;
const char *name;
unsigned long total_size;
unsigned long kmsg_size;
int max_reason;
unsigned long pmsg_size;
unsigned long console_size;
unsigned long ftrace_size;
pstore_zone_read_op read;
pstore_zone_write_op write;
pstore_zone_erase_op erase;
pstore_zone_write_op panic_write;
};
extern int register_pstore_zone(struct pstore_zone_info *info);
extern void unregister_pstore_zone(struct pstore_zone_info *info);
#endif
...@@ -3144,6 +3144,23 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister); ...@@ -3144,6 +3144,23 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
static bool always_kmsg_dump; static bool always_kmsg_dump;
module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR); module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR);
const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason)
{
switch (reason) {
case KMSG_DUMP_PANIC:
return "Panic";
case KMSG_DUMP_OOPS:
return "Oops";
case KMSG_DUMP_EMERG:
return "Emergency";
case KMSG_DUMP_SHUTDOWN:
return "Shutdown";
default:
return "Unknown";
}
}
EXPORT_SYMBOL_GPL(kmsg_dump_reason_str);
/** /**
* kmsg_dump - dump kernel log to kernel message dumpers. * kmsg_dump - dump kernel log to kernel message dumpers.
* @reason: the reason (oops, panic etc) for dumping * @reason: the reason (oops, panic etc) for dumping
...@@ -3157,12 +3174,19 @@ void kmsg_dump(enum kmsg_dump_reason reason) ...@@ -3157,12 +3174,19 @@ void kmsg_dump(enum kmsg_dump_reason reason)
struct kmsg_dumper *dumper; struct kmsg_dumper *dumper;
unsigned long flags; unsigned long flags;
if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
return;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(dumper, &dump_list, list) { list_for_each_entry_rcu(dumper, &dump_list, list) {
if (dumper->max_reason && reason > dumper->max_reason) enum kmsg_dump_reason max_reason = dumper->max_reason;
/*
* If client has not provided a specific max_reason, default
* to KMSG_DUMP_OOPS, unless always_kmsg_dump was set.
*/
if (max_reason == KMSG_DUMP_UNDEF) {
max_reason = always_kmsg_dump ? KMSG_DUMP_MAX :
KMSG_DUMP_OOPS;
}
if (reason > max_reason)
continue; continue;
/* initialize iterator with data about the stored records */ /* initialize iterator with data about the stored records */
......
...@@ -250,7 +250,7 @@ void kernel_restart(char *cmd) ...@@ -250,7 +250,7 @@ void kernel_restart(char *cmd)
pr_emerg("Restarting system\n"); pr_emerg("Restarting system\n");
else else
pr_emerg("Restarting system with command '%s'\n", cmd); pr_emerg("Restarting system with command '%s'\n", cmd);
kmsg_dump(KMSG_DUMP_RESTART); kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_restart(cmd); machine_restart(cmd);
} }
EXPORT_SYMBOL_GPL(kernel_restart); EXPORT_SYMBOL_GPL(kernel_restart);
...@@ -274,7 +274,7 @@ void kernel_halt(void) ...@@ -274,7 +274,7 @@ void kernel_halt(void)
migrate_to_reboot_cpu(); migrate_to_reboot_cpu();
syscore_shutdown(); syscore_shutdown();
pr_emerg("System halted\n"); pr_emerg("System halted\n");
kmsg_dump(KMSG_DUMP_HALT); kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_halt(); machine_halt();
} }
EXPORT_SYMBOL_GPL(kernel_halt); EXPORT_SYMBOL_GPL(kernel_halt);
...@@ -292,7 +292,7 @@ void kernel_power_off(void) ...@@ -292,7 +292,7 @@ void kernel_power_off(void)
migrate_to_reboot_cpu(); migrate_to_reboot_cpu();
syscore_shutdown(); syscore_shutdown();
pr_emerg("Power down\n"); pr_emerg("Power down\n");
kmsg_dump(KMSG_DUMP_POWEROFF); kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_power_off(); machine_power_off();
} }
EXPORT_SYMBOL_GPL(kernel_power_off); EXPORT_SYMBOL_GPL(kernel_power_off);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
. ./common_tests . ./common_tests
prlog -n "Checking pstore console is registered ... " prlog -n "Checking pstore console is registered ... "
dmesg | grep -q "console \[pstore" dmesg | grep -Eq "console \[(pstore|${backend})"
show_result $? show_result $?
prlog -n "Checking /dev/pmsg0 exists ... " prlog -n "Checking /dev/pmsg0 exists ... "
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment