[Mod] 同步v5.10版本Pstore

This commit is contained in:
gaoyang3513
2024-04-23 00:57:34 +08:00
parent ba555b1993
commit f84c84aa2b
15 changed files with 2856 additions and 593 deletions

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
config PSTORE
tristate "Persistent store support"
select CRYPTO if PSTORE_COMPRESS
@ -114,18 +115,6 @@ config PSTORE_CONSOLE
When the option is enabled, pstore will log all kernel
messages, even if no oops or panic happened.
config PSTORE_CONSOLE_FORCE
bool "Log kernel console messages ignore loglevel setting"
depends on PSTORE_CONSOLE
help
When the option is enabled, allow all kernel messages
log to the pstore console. Enable this with a kernel bool
parameter like "pstore_con_force=1".
config PSTORE_CONSOLE_FORCE_ON
bool "Log kernel console messages ignore loglevel setting on by default"
depends on PSTORE_CONSOLE_FORCE
config PSTORE_PMSG
bool "Log user space messages"
depends on PSTORE
@ -153,7 +142,6 @@ config PSTORE_RAM
tristate "Log panic/oops to a RAM buffer"
depends on PSTORE
depends on HAS_IOMEM
depends on HAVE_MEMBLOCK
select REED_SOLOMON
select REED_SOLOMON_ENC8
select REED_SOLOMON_DEC8
@ -166,6 +154,116 @@ config PSTORE_RAM
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
depends on BROKEN
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.
config PSTORE_BOOT_LOG
bool "Print boot log by linux"
depends on PSTORE

View File

@ -12,3 +12,9 @@ pstore-$(CONFIG_PSTORE_PMSG) += pmsg.o
ramoops-objs += ram.o ram_core.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

517
fs/pstore/blk.c Normal file
View File

@ -0,0 +1,517 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Implements pstore backend driver that write to block (or non-block) storage
* devices, using the pstore/zone API.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include "../../block/blk.h"
#include <linux/blkdev.h>
#include <linux/string.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pstore_blk.h>
#include <linux/mount.h>
#include <linux/uio.h>
static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
module_param(kmsg_size, long, 0400);
MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
module_param(max_reason, int, 0400);
MODULE_PARM_DESC(max_reason,
"maximum reason for kmsg dump (default 2: Oops and Panic)");
#if IS_ENABLED(CONFIG_PSTORE_PMSG)
static long pmsg_size = CONFIG_PSTORE_BLK_PMSG_SIZE;
#else
static long pmsg_size = -1;
#endif
module_param(pmsg_size, long, 0400);
MODULE_PARM_DESC(pmsg_size, "pmsg size in kbytes");
#if IS_ENABLED(CONFIG_PSTORE_CONSOLE)
static long console_size = CONFIG_PSTORE_BLK_CONSOLE_SIZE;
#else
static long console_size = -1;
#endif
module_param(console_size, long, 0400);
MODULE_PARM_DESC(console_size, "console size in kbytes");
#if IS_ENABLED(CONFIG_PSTORE_FTRACE)
static long ftrace_size = CONFIG_PSTORE_BLK_FTRACE_SIZE;
#else
static long ftrace_size = -1;
#endif
module_param(ftrace_size, long, 0400);
MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes");
static bool best_effort;
module_param(best_effort, bool, 0400);
MODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)");
/*
* blkdev - the block device to use for pstore storage
*
* Usually, this will be a partition of a block device.
*
* blkdev 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 number of disk
* 3) /dev/<disk_name><decimal> represents the device number
* of partition - device number of disk plus the partition number
* 4) /dev/<disk_name>p<decimal> - same as the above, that form is
* used when disk name of partitioned disk ends on 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.
*/
static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
module_param_string(blkdev, blkdev, 80, 0400);
MODULE_PARM_DESC(blkdev, "block device for pstore storage");
/*
* All globals must only be accessed under the pstore_blk_lock
* during the register/unregister functions.
*/
static DEFINE_MUTEX(pstore_blk_lock);
static struct block_device *psblk_bdev;
static struct pstore_zone_info *pstore_zone_info;
static pstore_blk_panic_write_op blkdev_panic_write;
struct bdev_info {
dev_t devt;
sector_t nr_sects;
sector_t start_sect;
};
#define check_size(name, alignsize) ({ \
long _##name_ = (name); \
_##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
if (_##name_ & ((alignsize) - 1)) { \
pr_info(#name " must align to %d\n", \
(alignsize)); \
_##name_ = ALIGN(name, (alignsize)); \
} \
_##name_; \
})
static int __register_pstore_device(struct pstore_device_info *dev)
{
int ret;
lockdep_assert_held(&pstore_blk_lock);
if (!dev || !dev->total_size || !dev->read || !dev->write)
return -EINVAL;
/* someone already registered before */
if (pstore_zone_info)
return -EBUSY;
pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
if (!pstore_zone_info)
return -ENOMEM;
/* zero means not limit on which backends to attempt to store. */
if (!dev->flags)
dev->flags = UINT_MAX;
#define verify_size(name, alignsize, enabled) { \
long _##name_; \
if (enabled) \
_##name_ = check_size(name, alignsize); \
else \
_##name_ = 0; \
name = _##name_ / 1024; \
pstore_zone_info->name = _##name_; \
}
verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
verify_size(pmsg_size, 4096, dev->flags & PSTORE_FLAGS_PMSG);
verify_size(console_size, 4096, dev->flags & PSTORE_FLAGS_CONSOLE);
verify_size(ftrace_size, 4096, dev->flags & PSTORE_FLAGS_FTRACE);
#undef verify_size
pstore_zone_info->total_size = dev->total_size;
pstore_zone_info->max_reason = max_reason;
pstore_zone_info->read = dev->read;
pstore_zone_info->write = dev->write;
pstore_zone_info->erase = dev->erase;
pstore_zone_info->panic_write = dev->panic_write;
pstore_zone_info->name = KBUILD_MODNAME;
pstore_zone_info->owner = THIS_MODULE;
ret = register_pstore_zone(pstore_zone_info);
if (ret) {
kfree(pstore_zone_info);
pstore_zone_info = NULL;
}
return ret;
}
/**
* register_pstore_device() - register non-block device to pstore/blk
*
* @dev: non-block device information
*
* Return:
* * 0 - OK
* * Others - something error.
*/
int register_pstore_device(struct pstore_device_info *dev)
{
int ret;
mutex_lock(&pstore_blk_lock);
ret = __register_pstore_device(dev);
mutex_unlock(&pstore_blk_lock);
return ret;
}
EXPORT_SYMBOL_GPL(register_pstore_device);
static void __unregister_pstore_device(struct pstore_device_info *dev)
{
lockdep_assert_held(&pstore_blk_lock);
if (pstore_zone_info && pstore_zone_info->read == dev->read) {
unregister_pstore_zone(pstore_zone_info);
kfree(pstore_zone_info);
pstore_zone_info = NULL;
}
}
/**
* unregister_pstore_device() - unregister non-block device from pstore/blk
*
* @dev: non-block device information
*/
void unregister_pstore_device(struct pstore_device_info *dev)
{
mutex_lock(&pstore_blk_lock);
__unregister_pstore_device(dev);
mutex_unlock(&pstore_blk_lock);
}
EXPORT_SYMBOL_GPL(unregister_pstore_device);
/**
* psblk_get_bdev() - open block device
*
* @holder: Exclusive holder identifier
* @info: Information about bdev to fill in
*
* Return: pointer to block device on success and others on error.
*
* On success, the returned block_device has reference count of one.
*/
static struct block_device *psblk_get_bdev(void *holder,
struct bdev_info *info)
{
struct block_device *bdev = ERR_PTR(-ENODEV);
fmode_t mode = FMODE_READ | FMODE_WRITE;
sector_t nr_sects;
lockdep_assert_held(&pstore_blk_lock);
if (pstore_zone_info)
return ERR_PTR(-EBUSY);
if (!blkdev[0])
return ERR_PTR(-ENODEV);
if (holder)
mode |= FMODE_EXCL;
bdev = blkdev_get_by_path(blkdev, mode, holder);
if (IS_ERR(bdev)) {
dev_t devt;
devt = name_to_dev_t(blkdev);
if (devt == 0)
return ERR_PTR(-ENODEV);
bdev = blkdev_get_by_dev(devt, mode, holder);
if (IS_ERR(bdev))
return bdev;
}
nr_sects = part_nr_sects_read(bdev->bd_part);
if (!nr_sects) {
pr_err("not enough space for '%s'\n", blkdev);
blkdev_put(bdev, mode);
return ERR_PTR(-ENOSPC);
}
if (info) {
info->devt = bdev->bd_dev;
info->nr_sects = nr_sects;
info->start_sect = get_start_sect(bdev);
}
return bdev;
}
static void psblk_put_bdev(struct block_device *bdev, void *holder)
{
fmode_t mode = FMODE_READ | FMODE_WRITE;
lockdep_assert_held(&pstore_blk_lock);
if (!bdev)
return;
if (holder)
mode |= FMODE_EXCL;
blkdev_put(bdev, mode);
}
static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
{
struct block_device *bdev = psblk_bdev;
struct file file;
struct kiocb kiocb;
struct iov_iter iter;
struct kvec iov = {.iov_base = buf, .iov_len = bytes};
if (!bdev)
return -ENODEV;
memset(&file, 0, sizeof(struct file));
file.f_mapping = bdev->bd_inode->i_mapping;
file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
file.f_inode = bdev->bd_inode;
file_ra_state_init(&file.f_ra, file.f_mapping);
init_sync_kiocb(&kiocb, &file);
kiocb.ki_pos = pos;
iov_iter_kvec(&iter, READ, &iov, 1, bytes);
return generic_file_read_iter(&kiocb, &iter);
}
static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
loff_t pos)
{
struct block_device *bdev = psblk_bdev;
struct iov_iter iter;
struct kiocb kiocb;
struct file file;
ssize_t ret;
struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
if (!bdev)
return -ENODEV;
/* Console/Ftrace backend may handle buffer until flush dirty zones */
if (in_interrupt() || irqs_disabled())
return -EBUSY;
memset(&file, 0, sizeof(struct file));
file.f_mapping = bdev->bd_inode->i_mapping;
file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
file.f_inode = bdev->bd_inode;
init_sync_kiocb(&kiocb, &file);
kiocb.ki_pos = pos;
iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
inode_lock(bdev->bd_inode);
ret = generic_write_checks(&kiocb, &iter);
if (ret > 0)
ret = generic_perform_write(&file, &iter, pos);
inode_unlock(bdev->bd_inode);
if (likely(ret > 0)) {
const struct file_operations f_op = {.fsync = blkdev_fsync};
file.f_op = &f_op;
kiocb.ki_pos += ret;
ret = generic_write_sync(&kiocb, ret);
}
return ret;
}
static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
loff_t off)
{
int ret;
if (!blkdev_panic_write)
return -EOPNOTSUPP;
/* size and off must align to SECTOR_SIZE for block device */
ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
size >> SECTOR_SHIFT);
/* try next zone */
if (ret == -ENOMSG)
return ret;
return ret ? -EIO : size;
}
static int __register_pstore_blk(struct pstore_blk_info *info)
{
char bdev_name[BDEVNAME_SIZE];
struct block_device *bdev;
struct pstore_device_info dev;
struct bdev_info binfo;
void *holder = blkdev;
int ret = -ENODEV;
lockdep_assert_held(&pstore_blk_lock);
/* hold bdev exclusively */
memset(&binfo, 0, sizeof(binfo));
bdev = psblk_get_bdev(holder, &binfo);
if (IS_ERR(bdev)) {
pr_err("failed to open '%s'!\n", blkdev);
return PTR_ERR(bdev);
}
/* only allow driver matching the @blkdev */
if (!binfo.devt || (!best_effort &&
MAJOR(binfo.devt) != info->major)) {
pr_debug("invalid major %u (expect %u)\n",
info->major, MAJOR(binfo.devt));
ret = -ENODEV;
goto err_put_bdev;
}
/* psblk_bdev must be assigned before register to pstore/blk */
psblk_bdev = bdev;
blkdev_panic_write = info->panic_write;
/* Copy back block device details. */
info->devt = binfo.devt;
info->nr_sects = binfo.nr_sects;
info->start_sect = binfo.start_sect;
memset(&dev, 0, sizeof(dev));
dev.total_size = info->nr_sects << SECTOR_SHIFT;
dev.flags = info->flags;
dev.read = psblk_generic_blk_read;
dev.write = psblk_generic_blk_write;
dev.erase = NULL;
dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
ret = __register_pstore_device(&dev);
if (ret)
goto err_put_bdev;
bdevname(bdev, bdev_name);
pr_info("attached %s%s\n", bdev_name,
info->panic_write ? "" : " (no dedicated panic_write!)");
return 0;
err_put_bdev:
psblk_bdev = NULL;
blkdev_panic_write = NULL;
psblk_put_bdev(bdev, holder);
return ret;
}
/**
* register_pstore_blk() - register block device to pstore/blk
*
* @info: details on the desired block device interface
*
* Return:
* * 0 - OK
* * Others - something error.
*/
int register_pstore_blk(struct pstore_blk_info *info)
{
int ret;
mutex_lock(&pstore_blk_lock);
ret = __register_pstore_blk(info);
mutex_unlock(&pstore_blk_lock);
return ret;
}
EXPORT_SYMBOL_GPL(register_pstore_blk);
static void __unregister_pstore_blk(unsigned int major)
{
struct pstore_device_info dev = { .read = psblk_generic_blk_read };
void *holder = blkdev;
lockdep_assert_held(&pstore_blk_lock);
if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
__unregister_pstore_device(&dev);
psblk_put_bdev(psblk_bdev, holder);
blkdev_panic_write = NULL;
psblk_bdev = NULL;
}
}
/**
* unregister_pstore_blk() - unregister block device from pstore/blk
*
* @major: the major device number of device
*/
void unregister_pstore_blk(unsigned int major)
{
mutex_lock(&pstore_blk_lock);
__unregister_pstore_blk(major);
mutex_unlock(&pstore_blk_lock);
}
EXPORT_SYMBOL_GPL(unregister_pstore_blk);
/* get information of pstore/blk */
int pstore_blk_get_config(struct pstore_blk_config *info)
{
strncpy(info->device, blkdev, 80);
info->max_reason = max_reason;
info->kmsg_size = check_size(kmsg_size, 4096);
info->pmsg_size = check_size(pmsg_size, 4096);
info->ftrace_size = check_size(ftrace_size, 4096);
info->console_size = check_size(console_size, 4096);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_blk_get_config);
static int __init pstore_blk_init(void)
{
struct pstore_blk_info info = { };
int ret = 0;
mutex_lock(&pstore_blk_lock);
if (!pstore_zone_info && best_effort && blkdev[0])
ret = __register_pstore_blk(&info);
mutex_unlock(&pstore_blk_lock);
return ret;
}
late_initcall(pstore_blk_init);
static void __exit pstore_blk_exit(void)
{
mutex_lock(&pstore_blk_lock);
if (psblk_bdev)
__unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev));
else {
struct pstore_device_info dev = { };
if (pstore_zone_info)
dev.read = pstore_zone_info->read;
__unregister_pstore_device(&dev);
}
mutex_unlock(&pstore_blk_lock);
}
module_exit(pstore_blk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
MODULE_DESCRIPTION("pstore backend for block devices");

View File

@ -1,14 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2012 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
@ -24,6 +16,7 @@
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/cache.h>
#include <linux/slab.h>
#include <asm/barrier.h>
#include "internal.h"
@ -120,27 +113,13 @@ static struct dentry *pstore_ftrace_dir;
void pstore_register_ftrace(void)
{
struct dentry *file;
if (!psinfo->write)
return;
pstore_ftrace_dir = debugfs_create_dir("pstore", NULL);
if (!pstore_ftrace_dir) {
pr_err("%s: unable to create pstore directory\n", __func__);
return;
}
file = debugfs_create_file("record_ftrace", 0600, pstore_ftrace_dir,
NULL, &pstore_knob_fops);
if (!file) {
pr_err("%s: unable to create record_ftrace file\n", __func__);
goto err_file;
}
return;
err_file:
debugfs_remove(pstore_ftrace_dir);
debugfs_create_file("record_ftrace", 0600, pstore_ftrace_dir, NULL,
&pstore_knob_fops);
}
void pstore_unregister_ftrace(void)
@ -148,9 +127,62 @@ void pstore_unregister_ftrace(void)
mutex_lock(&pstore_ftrace_lock);
if (pstore_ftrace_enabled) {
unregister_ftrace_function(&pstore_ftrace_ops);
pstore_ftrace_enabled = 0;
pstore_ftrace_enabled = false;
}
mutex_unlock(&pstore_ftrace_lock);
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);

View File

@ -1,20 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Persistent Storage - ramfs parts.
*
* Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
@ -34,7 +22,6 @@
#include <linux/magic.h>
#include <linux/pstore.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#ifdef CONFIG_PSTORE_BOOT_LOG
@ -46,11 +33,15 @@
#define PSTORE_NAMELEN 64
static DEFINE_SPINLOCK(allpstore_lock);
static LIST_HEAD(allpstore);
static DEFINE_MUTEX(records_list_lock);
static LIST_HEAD(records_list);
static DEFINE_MUTEX(pstore_sb_lock);
static struct super_block *pstore_sb;
struct pstore_private {
struct list_head list;
struct dentry *dentry;
struct pstore_record *record;
size_t total_size;
};
@ -123,7 +114,7 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off);
seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %pf <- %pF\n",
seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %ps <- %pS\n",
pstore_ftrace_decode_cpu(rec),
pstore_ftrace_read_timestamp(rec),
rec->ip, rec->parent_ip, (void *)rec->ip,
@ -145,39 +136,13 @@ static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
struct seq_file *sf = file->private_data;
struct pstore_private *ps = sf->private;
#ifdef CONFIG_PSTORE_BOOT_LOG
size_t size = 0;
struct pstore_record *record = ps->record;
struct ramoops_context *cxt = record->psi->data;
struct persistent_ram_zone *prz;
struct persistent_ram_buffer *buffer;
char *log_tmp;
size_t size, start, n;
if (ps->record->type == PSTORE_TYPE_BOOT_LOG) {
if (!cxt)
return count;
prz = cxt->boot_przs[record->id];
if (!prz)
return count;
buffer = prz->buffer;
if (!buffer)
return count;
size = atomic_read(&buffer->size);
start = atomic_read(&buffer->start);
log_tmp = kmalloc(size, GFP_KERNEL);
if (!log_tmp)
return count;
memcpy_fromio(log_tmp, &buffer->data[start], size - start);
memcpy_fromio(log_tmp + size - start, &buffer->data[0], start);
n = simple_read_from_buffer(userbuf, count, ppos, log_tmp, size);
kfree(log_tmp);
return n;
if (record->type == PSTORE_TYPE_BOOT_LOG) {
size = ramoops_pstore_read_for_boot_log(ps->record);
size = simple_read_from_buffer(userbuf, count, ppos, record->buf, size);
return size;
}
#endif
if (ps->record->type == PSTORE_TYPE_FTRACE)
@ -230,10 +195,22 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{
struct pstore_private *p = d_inode(dentry)->i_private;
struct pstore_record *record = p->record;
int rc = 0;
if (!record->psi->erase)
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);
record->psi->erase(record);
mutex_unlock(&record->psi->read_mutex);
@ -244,15 +221,9 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
static void pstore_evict_inode(struct inode *inode)
{
struct pstore_private *p = inode->i_private;
unsigned long flags;
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 = {
@ -330,11 +301,54 @@ static const struct super_operations pstore_ops = {
.show_options = pstore_show_options,
};
static struct super_block *pstore_sb;
bool pstore_is_mounted(void)
static struct dentry *psinfo_lock_root(void)
{
return pstore_sb != NULL;
struct dentry *root;
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)
{
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;
}
/*
@ -349,23 +363,20 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
int rc = 0;
char name[PSTORE_NAMELEN];
struct pstore_private *private, *pos;
unsigned long flags;
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);
list_for_each_entry(pos, &allpstore, list) {
rc = -EEXIST;
/* 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 &&
pos->record->id == record->id &&
pos->record->psi == record->psi) {
rc = -EEXIST;
break;
}
pos->record->psi == record->psi)
goto fail;
}
spin_unlock_irqrestore(&allpstore_lock, flags);
if (rc)
return rc;
rc = -ENOMEM;
inode = pstore_get_inode(root->d_sb);
@ -373,59 +384,10 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
goto fail;
inode->i_mode = S_IFREG | 0444;
inode->i_fop = &pstore_file_operations;
switch (record->type) {
case PSTORE_TYPE_DMESG:
scnprintf(name, sizeof(name), "dmesg-%s-%llu%s",
record->psi->name, record->id,
record->compressed ? ".enc.z" : "");
break;
case PSTORE_TYPE_CONSOLE:
scnprintf(name, sizeof(name), "console-%s-%llu",
record->psi->name, record->id);
break;
case PSTORE_TYPE_FTRACE:
scnprintf(name, sizeof(name), "ftrace-%s-%llu",
record->psi->name, record->id);
break;
case PSTORE_TYPE_MCE:
scnprintf(name, sizeof(name), "mce-%s-%llu",
record->psi->name, record->id);
break;
case PSTORE_TYPE_PPC_RTAS:
scnprintf(name, sizeof(name), "rtas-%s-%llu",
record->psi->name, record->id);
break;
case PSTORE_TYPE_PPC_OF:
scnprintf(name, sizeof(name), "powerpc-ofw-%s-%llu",
record->psi->name, record->id);
break;
case PSTORE_TYPE_PPC_COMMON:
scnprintf(name, sizeof(name), "powerpc-common-%s-%llu",
record->psi->name, record->id);
break;
case PSTORE_TYPE_PMSG:
scnprintf(name, sizeof(name), "pmsg-%s-%llu",
record->psi->name, record->id);
break;
case PSTORE_TYPE_PPC_OPAL:
scnprintf(name, sizeof(name), "powerpc-opal-%s-%llu",
record->psi->name, record->id);
break;
#ifdef CONFIG_PSTORE_BOOT_LOG
case PSTORE_TYPE_BOOT_LOG:
scnprintf(name, sizeof(name), "boot-log-%llu", record->id);
break;
#endif
case PSTORE_TYPE_UNKNOWN:
scnprintf(name, sizeof(name), "unknown-%s-%llu",
record->psi->name, record->id);
break;
default:
scnprintf(name, sizeof(name), "type%d-%s-%llu",
record->type, record->psi->name, record->id);
break;
}
scnprintf(name, sizeof(name), "%s-%s-%llu%s",
pstore_type_to_name(record->type),
record->psi->name, record->id,
record->compressed ? ".enc.z" : "");
private = kzalloc(sizeof(*private), GFP_KERNEL);
if (!private)
@ -435,6 +397,7 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
if (!dentry)
goto fail_private;
private->dentry = dentry;
private->record = record;
inode->i_size = private->total_size = size;
inode->i_private = private;
@ -444,9 +407,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
d_add(dentry, inode);
spin_lock_irqsave(&allpstore_lock, flags);
list_add(&private->list, &allpstore);
spin_unlock_irqrestore(&allpstore_lock, flags);
list_add(&private->list, &records_list);
mutex_unlock(&records_list_lock);
return 0;
@ -454,8 +416,8 @@ fail_private:
free_pstore_private(private);
fail_inode:
iput(inode);
fail:
mutex_unlock(&records_list_lock);
return rc;
}
@ -467,16 +429,13 @@ fail:
*/
void pstore_get_records(int quiet)
{
struct pstore_info *psi = psinfo;
struct dentry *root;
if (!psi || !pstore_sb)
root = psinfo_lock_root();
if (!root)
return;
root = pstore_sb->s_root;
inode_lock(d_inode(root));
pstore_get_backend_records(psi, root, quiet);
pstore_get_backend_records(psinfo, root, quiet);
inode_unlock(d_inode(root));
}
@ -484,8 +443,6 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
pstore_sb = sb;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
@ -506,6 +463,10 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
if (!sb->s_root)
return -ENOMEM;
mutex_lock(&pstore_sb_lock);
pstore_sb = sb;
mutex_unlock(&pstore_sb_lock);
pstore_get_records(0);
return 0;
@ -519,8 +480,17 @@ static struct dentry *pstore_mount(struct file_system_type *fs_type,
static void pstore_kill_sb(struct super_block *sb)
{
mutex_lock(&pstore_sb_lock);
WARN_ON(pstore_sb && pstore_sb != sb);
kill_litter_super(sb);
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 = {

View File

@ -12,9 +12,18 @@ extern unsigned long kmsg_bytes;
#ifdef CONFIG_PSTORE_FTRACE
extern void pstore_register_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
static inline void pstore_register_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
#ifdef CONFIG_PSTORE_PMSG
@ -31,9 +40,9 @@ extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(int);
extern void pstore_get_backend_records(struct pstore_info *psi,
struct dentry *root, int quiet);
extern int pstore_put_backend_records(struct pstore_info *psi);
extern int pstore_mkfile(struct dentry *root,
struct pstore_record *record);
extern bool pstore_is_mounted(void);
extern void pstore_record_init(struct pstore_record *record,
struct pstore_info *psi);

View File

@ -1,21 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Persistent Storage - platform driver interface parts.
*
* Copyright (C) 2007-2008 Google, Inc.
* Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define pr_fmt(fmt) "pstore: " fmt
@ -56,9 +44,25 @@ static int pstore_update_ms = -1;
module_param_named(update_ms, pstore_update_ms, int, 0600);
MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content "
"(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)");
/* Names should be in the same order as the enum pstore_type_id */
static const char * const pstore_type_names[] = {
"dmesg",
"mce",
"console",
"ftrace",
"rtas",
"powerpc-ofw",
"powerpc-common",
"pmsg",
"powerpc-opal",
#ifdef CONFIG_PSTORE_BOOT_LOG
"boot-log",
#endif
};
static int pstore_new_entry;
static void pstore_timefunc(struct timer_list *);
@ -68,19 +72,25 @@ static void pstore_dowork(struct work_struct *);
static DECLARE_WORK(pstore_work, pstore_dowork);
/*
* pstore_lock just protects "psinfo" during
* calls to pstore_register()
* psinfo_lock protects "psinfo" during calls to
* pstore_register(), pstore_unregister(), and
* the filesystem mount/unmount routines.
*/
static DEFINE_SPINLOCK(pstore_lock);
static DEFINE_MUTEX(psinfo_lock);
struct pstore_info *psinfo;
static char *backend;
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "specific backend to use");
static char *compress =
#ifdef CONFIG_PSTORE_COMPRESS_DEFAULT
CONFIG_PSTORE_COMPRESS_DEFAULT;
#else
NULL;
#endif
module_param(compress, charp, 0444);
MODULE_PARM_DESC(compress, "compression to use");
/* Compression parameters */
static struct crypto_comp *tfm;
@ -104,24 +114,36 @@ void pstore_set_kmsg_bytes(int bytes)
/* Tag each group of saved records with a sequence number */
static int oopscount;
static const char *get_reason_str(enum kmsg_dump_reason reason)
const char *pstore_type_to_name(enum pstore_type_id type)
{
switch (reason) {
case KMSG_DUMP_PANIC:
return "Panic";
case KMSG_DUMP_OOPS:
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";
BUILD_BUG_ON(ARRAY_SIZE(pstore_type_names) != PSTORE_TYPE_MAX);
if (WARN_ON_ONCE(type >= PSTORE_TYPE_MAX))
return "unknown";
return pstore_type_names[type];
}
EXPORT_SYMBOL_GPL(pstore_type_to_name);
enum pstore_type_id pstore_name_to_type(const char *name)
{
int i;
for (i = 0; i < PSTORE_TYPE_MAX; i++) {
if (!strcmp(pstore_type_names[i], name))
return i;
}
return PSTORE_TYPE_MAX;
}
EXPORT_SYMBOL_GPL(pstore_name_to_type);
static void pstore_timer_kick(void)
{
if (pstore_update_ms < 0)
return;
mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
}
/*
@ -262,20 +284,6 @@ static int pstore_compress(const void *in, void *out,
return outlen;
}
static int pstore_decompress(void *in, void *out,
unsigned int inlen, unsigned int outlen)
{
int ret;
ret = crypto_comp_decompress(tfm, in, inlen, out, &outlen);
if (ret) {
pr_err("crypto_comp_decompress failed, ret = %d!\n", ret);
return ret;
}
return outlen;
}
static void allocate_buf_for_compression(void)
{
struct crypto_comp *ctx;
@ -322,7 +330,7 @@ static void allocate_buf_for_compression(void)
big_oops_buf_sz = size;
big_oops_buf = buf;
pr_info("Using compression: %s\n", zbackend->name);
pr_info("Using crash dump compression: %s\n", zbackend->name);
}
static void free_buf_for_compression(void)
@ -374,9 +382,8 @@ void pstore_record_init(struct pstore_record *record,
}
/*
* callback from kmsg_dump. (s2,l2) has the most recently
* written bytes, older bytes are in (s1,l1). Save as much
* as we can from the end of the buffer.
* callback from kmsg_dump. Save as much as we can (up to kmsg_bytes) from the
* end of the buffer.
*/
static void pstore_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason)
@ -386,7 +393,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
unsigned int part = 1;
int ret;
why = get_reason_str(reason);
why = kmsg_dump_reason_str(reason);
if (down_trylock(&psinfo->buf_lock)) {
/* Failed to acquire lock: give up if we cannot wait. */
@ -452,8 +459,10 @@ static void pstore_dump(struct kmsg_dumper *dumper,
}
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_timer_kick();
}
total += record.size;
part++;
@ -484,6 +493,9 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
{
struct pstore_record record;
if (!c)
return;
pstore_record_init(&record, psinfo);
record.type = PSTORE_TYPE_CONSOLE;
@ -493,18 +505,20 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
}
static struct console pstore_console = {
.name = "pstore",
.write = pstore_console_write,
#ifdef CON_PSTORE
.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME | CON_PSTORE,
#else
.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME,
#endif
.index = -1,
};
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);
}
@ -549,8 +563,6 @@ out:
*/
int pstore_register(struct pstore_info *psi)
{
struct module *owner = psi->owner;
if (backend && strcmp(backend, psi->name)) {
pr_warn("ignoring unexpected backend '%s'\n", psi->name);
return -EPERM;
@ -570,11 +582,11 @@ int pstore_register(struct pstore_info *psi)
return -EINVAL;
}
spin_lock(&pstore_lock);
mutex_lock(&psinfo_lock);
if (psinfo) {
pr_warn("backend '%s' already loaded: ignoring '%s'\n",
psinfo->name, psi->name);
spin_unlock(&pstore_lock);
mutex_unlock(&psinfo_lock);
return -EBUSY;
}
@ -583,21 +595,16 @@ int pstore_register(struct pstore_info *psi)
psinfo = psi;
mutex_init(&psinfo->read_mutex);
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)
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();
}
if (psi->flags & PSTORE_FLAGS_CONSOLE)
pstore_register_console();
if (psi->flags & PSTORE_FLAGS_FTRACE)
@ -606,33 +613,36 @@ int pstore_register(struct pstore_info *psi)
pstore_register_pmsg();
/* Start watching for new records, if desired. */
if (pstore_update_ms >= 0) {
pstore_timer.expires = jiffies +
msecs_to_jiffies(pstore_update_ms);
add_timer(&pstore_timer);
}
pstore_timer_kick();
/*
* Update the module parameter backend, so it is visible
* 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);
module_put(owner);
mutex_unlock(&psinfo_lock);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);
void pstore_unregister(struct pstore_info *psi)
{
/* Stop timer and make sure all work has finished. */
pstore_update_ms = -1;
del_timer_sync(&pstore_timer);
flush_work(&pstore_work);
/* It's okay to unregister nothing. */
if (!psi)
return;
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)
pstore_unregister_pmsg();
if (psi->flags & PSTORE_FLAGS_FTRACE)
@ -642,17 +652,27 @@ void pstore_unregister(struct pstore_info *psi)
if (psi->flags & PSTORE_FLAGS_DMESG)
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();
psinfo = NULL;
kfree(backend);
backend = NULL;
mutex_unlock(&psinfo_lock);
}
EXPORT_SYMBOL_GPL(pstore_unregister);
static void decompress_record(struct pstore_record *record)
{
int ret;
int unzipped_len;
char *decompressed;
char *unzipped, *workspace;
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !record->compressed)
return;
@ -663,35 +683,42 @@ static void decompress_record(struct pstore_record *record)
return;
}
/* No compression method has created the common buffer. */
/* Missing compression buffer means compression was not initialized. */
if (!big_oops_buf) {
pr_warn("no decompression buffer allocated\n");
pr_warn("no decompression method initialized!\n");
return;
}
unzipped_len = pstore_decompress(record->buf, big_oops_buf,
record->size, big_oops_buf_sz);
if (unzipped_len <= 0) {
pr_err("decompression failed: %d\n", unzipped_len);
/* Allocate enough space to hold max decompression and ECC. */
unzipped_len = big_oops_buf_sz;
workspace = kmalloc(unzipped_len + record->ecc_notice_size,
GFP_KERNEL);
if (!workspace)
return;
}
/* Build new buffer for decompressed contents. */
decompressed = kmalloc(unzipped_len + record->ecc_notice_size,
GFP_KERNEL);
if (!decompressed) {
pr_err("decompression ran out of memory\n");
/* After decompression "unzipped_len" is almost certainly smaller. */
ret = crypto_comp_decompress(tfm, record->buf, record->size,
workspace, &unzipped_len);
if (ret) {
pr_err("crypto_comp_decompress failed, ret = %d!\n", ret);
kfree(workspace);
return;
}
memcpy(decompressed, big_oops_buf, unzipped_len);
/* Append ECC notice to decompressed buffer. */
memcpy(decompressed + unzipped_len, record->buf + record->size,
memcpy(workspace + unzipped_len, record->buf + record->size,
record->ecc_notice_size);
/* Swap out compresed contents with decompressed contents. */
/* Copy decompressed contents into an minimum-sized allocation. */
unzipped = kmemdup(workspace, unzipped_len + record->ecc_notice_size,
GFP_KERNEL);
kfree(workspace);
if (!unzipped)
return;
/* Swap out compressed contents with decompressed contents. */
kfree(record->buf);
record->buf = decompressed;
record->buf = unzipped;
record->size = unzipped_len;
record->compressed = false;
}
@ -774,12 +801,10 @@ static void pstore_timefunc(struct timer_list *unused)
schedule_work(&pstore_work);
}
if (pstore_update_ms >= 0)
mod_timer(&pstore_timer,
jiffies + msecs_to_jiffies(pstore_update_ms));
pstore_timer_kick();
}
void __init pstore_choose_compression(void)
static void __init pstore_choose_compression(void)
{
const struct pstore_zbackend *step;
@ -821,11 +846,5 @@ static void __exit pstore_exit(void)
}
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_LICENSE("GPL");

View File

@ -1,14 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/cdev.h>
@ -33,7 +25,7 @@ static ssize_t write_pmsg(struct file *file, const char __user *buf,
record.size = count;
/* check outside lock, page in any data. write_user also checks */
if (!access_ok(VERIFY_READ, buf, count))
if (!access_ok(buf, count))
return -EFAULT;
mutex_lock(&pmsg_lock);

View File

@ -1,23 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* RAM Oops/Panic logger
*
* Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
* Copyright (C) 2011 Kees Cook <keescook@chromium.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -35,6 +21,12 @@
#include <linux/pstore_ram.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include "internal.h"
#if IS_REACHABLE(CONFIG_ROCKCHIP_MINIDUMP)
#include <soc/rockchip/rk_minidump.h>
#endif
#define RAMOOPS_KERNMSG_HDR "===="
#define MIN_MEM_SIZE 4096UL
@ -67,24 +59,63 @@ MODULE_PARM_DESC(mem_size,
"size of reserved RAM used to store oops/panic logs");
static unsigned int mem_type;
module_param(mem_type, uint, 0600);
module_param(mem_type, uint, 0400);
MODULE_PARM_DESC(mem_type,
"set to 1 to try to use unbuffered memory (default 0)");
"memory type: 0=write-combined (default), 1=unbuffered, 2=cached");
static int dump_oops = 1;
module_param(dump_oops, int, 0600);
MODULE_PARM_DESC(dump_oops,
"set to 1 to dump oopses, 0 to only dump panics (default 1)");
static int ramoops_max_reason = -1;
module_param_named(max_reason, ramoops_max_reason, int, 0400);
MODULE_PARM_DESC(max_reason,
"maximum reason for kmsg dump (default 2: Oops and Panic) ");
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,
"if non-zero, the option enables ECC support and specifies "
"ECC buffer size in bytes (1 is a special value, means 16 "
"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 persistent_ram_zone **dprzs; /* Oops dump zones */
struct persistent_ram_zone *cprz; /* Console zone */
struct persistent_ram_zone **fprzs; /* Ftrace zones */
struct persistent_ram_zone *mprz; /* PMSG zone */
#ifdef CONFIG_PSTORE_BOOT_LOG
struct persistent_ram_zone **boot_przs; /* BOOT log zones */
#endif
phys_addr_t phys_addr;
unsigned long size;
unsigned int memtype;
size_t record_size;
size_t console_size;
size_t ftrace_size;
size_t pmsg_size;
#ifdef CONFIG_PSTORE_BOOT_LOG
size_t boot_log_size;
#endif
u32 flags;
struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt;
unsigned int dump_write_cnt;
/* _read_cnt need clear on ramoops_pstore_open */
unsigned int dump_read_cnt;
unsigned int console_read_cnt;
unsigned int max_ftrace_cnt;
unsigned int ftrace_read_cnt;
unsigned int pmsg_read_cnt;
#ifdef CONFIG_PSTORE_BOOT_LOG
unsigned int boot_log_read_cnt;
unsigned int max_boot_log_cnt;
#endif
struct pstore_info pstore;
};
static struct platform_device *dummy;
static struct ramoops_platform_data *dummy_data;
static int ramoops_pstore_open(struct pstore_info *psi)
{
@ -98,31 +129,28 @@ static int ramoops_pstore_open(struct pstore_info *psi)
}
static struct persistent_ram_zone *
ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
u64 *id,
enum pstore_type_id *typep, enum pstore_type_id type,
bool update)
ramoops_get_next_prz(struct persistent_ram_zone *przs[], int id,
struct pstore_record *record)
{
struct persistent_ram_zone *prz;
int i = (*c)++;
/* Give up if we never existed or have hit the end. */
if (!przs || i >= max)
if (!przs)
return NULL;
prz = przs[i];
prz = przs[id];
if (!prz)
return NULL;
/* Update old/shadowed buffer. */
if (update)
if (prz->type == PSTORE_TYPE_DMESG)
persistent_ram_save_old(prz);
if (!persistent_ram_old_size(prz))
return NULL;
*typep = type;
*id = i;
record->type = prz->type;
record->id = id;
return prz;
}
@ -160,57 +188,27 @@ static bool prz_ok(struct persistent_ram_zone *prz)
persistent_ram_ecc_string(prz, NULL, 0));
}
static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest,
struct persistent_ram_zone *src)
#ifdef CONFIG_PSTORE_BOOT_LOG
ssize_t ramoops_pstore_read_for_boot_log(struct pstore_record *record)
{
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);
struct ramoops_context *cxt = record->psi->data;
struct persistent_ram_zone *prz;
dest_off = dest->old_log_size % record_size;
dest_size = dest->old_log_size - dest_off;
if (!cxt)
return 0;
src_off = src->old_log_size % record_size;
src_size = src->old_log_size - src_off;
prz = cxt->boot_przs[record->id];
total = dest_size + src_size;
merged_buf = kmalloc(total, GFP_KERNEL);
if (!merged_buf)
return -ENOMEM;
if (!prz)
return 0;
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;
persistent_ram_free_old(prz);
persistent_ram_save_old(prz);
record->buf = prz->old_log;
record->size = prz->old_log_size;
return record->size;
}
#endif
static ssize_t ramoops_pstore_read(struct pstore_record *record)
{
@ -231,10 +229,8 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
/* Find the next valid persistent_ram_zone for DMESG */
while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) {
prz = ramoops_get_next_prz(cxt->dprzs, &cxt->dump_read_cnt,
cxt->max_dump_cnt, &record->id,
&record->type,
PSTORE_TYPE_DMESG, 1);
prz = ramoops_get_next_prz(cxt->dprzs, cxt->dump_read_cnt++,
record);
if (!prz_ok(prz))
continue;
header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz),
@ -248,22 +244,18 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
}
}
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
1, &record->id, &record->type,
PSTORE_TYPE_CONSOLE, 0);
if (!prz_ok(prz) && !cxt->console_read_cnt++)
prz = ramoops_get_next_prz(&cxt->cprz, 0 /* single */, record);
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
1, &record->id, &record->type,
PSTORE_TYPE_PMSG, 0);
if (!prz_ok(prz) && !cxt->pmsg_read_cnt++)
prz = ramoops_get_next_prz(&cxt->mprz, 0 /* single */, record);
/* ftrace is last since it may want to dynamically allocate memory. */
if (!prz_ok(prz)) {
if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)) {
prz = ramoops_get_next_prz(cxt->fprzs,
&cxt->ftrace_read_cnt, 1, &record->id,
&record->type, PSTORE_TYPE_FTRACE, 0);
if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU) &&
!cxt->ftrace_read_cnt++) {
prz = ramoops_get_next_prz(cxt->fprzs, 0 /* single */,
record);
} else {
/*
* Build a new dummy record which combines all the
@ -280,11 +272,7 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
while (cxt->ftrace_read_cnt < cxt->max_ftrace_cnt) {
prz_next = ramoops_get_next_prz(cxt->fprzs,
&cxt->ftrace_read_cnt,
cxt->max_ftrace_cnt,
&record->id,
&record->type,
PSTORE_TYPE_FTRACE, 0);
cxt->ftrace_read_cnt++, record);
if (!prz_ok(prz_next))
continue;
@ -293,7 +281,12 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
tmp_prz->corrected_bytes +=
prz_next->corrected_bytes;
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)
goto out;
}
@ -304,10 +297,7 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
#ifdef CONFIG_PSTORE_BOOT_LOG
if (!prz_ok(prz)) {
while (cxt->boot_log_read_cnt < cxt->max_boot_log_cnt && !prz) {
prz = ramoops_get_next_prz(cxt->boot_przs, &cxt->boot_log_read_cnt,
cxt->max_boot_log_cnt, &record->id,
&record->type,
PSTORE_TYPE_BOOT_LOG, 0);
prz = ramoops_get_next_prz(cxt->boot_przs, cxt->boot_log_read_cnt++, record);
if (!prz_ok(prz))
continue;
}
@ -319,12 +309,15 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
goto out;
}
size = persistent_ram_old_size(prz) - header_length;
#ifdef CONFIG_PSTORE_BOOT_LOG
/* don't copy boot log */
if (record->type == PSTORE_TYPE_BOOT_LOG)
goto out;
if (record->type == PSTORE_TYPE_BOOT_LOG) {
persistent_ram_free_old(prz);
persistent_ram_save_old(prz);
}
#endif
size = persistent_ram_old_size(prz) - header_length;
/* ECC correction notice */
record->ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
@ -352,17 +345,15 @@ out:
static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
struct pstore_record *record)
{
char *hdr;
char hdr[36]; /* "===="(4), %lld(20), "."(1), %06lu(6), "-%c\n"(3) */
size_t len;
hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lld.%06lu-%c\n",
len = scnprintf(hdr, sizeof(hdr),
RAMOOPS_KERNMSG_HDR "%lld.%06lu-%c\n",
(time64_t)record->time.tv_sec,
record->time.tv_nsec / 1000,
record->compressed ? 'C' : 'D');
WARN_ON_ONCE(!hdr);
len = hdr ? strlen(hdr) : 0;
persistent_ram_write(prz, hdr, len);
kfree(hdr);
return len;
}
@ -403,16 +394,14 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
return -EINVAL;
/*
* Out of the various dmesg dump types, ramoops is currently designed
* to only store crash logs, rather than storing general kernel logs.
* We could filter on record->reason here if we wanted to (which
* 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.
@ -441,6 +430,9 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
/* Build header and append record contents. */
hlen = ramoops_write_kmsg_hdr(prz, record);
if (!hlen)
return -ENOMEM;
size = record->size;
if (size + hlen > prz->buffer_size)
size = prz->buffer_size - hlen;
@ -600,9 +592,17 @@ static int ramoops_init_przs(const char *name,
goto fail;
for (i = 0; i < *cnt; i++) {
char *label;
if (*cnt == 1)
label = kasprintf(GFP_KERNEL, "ramoops:%s", name);
else
label = kasprintf(GFP_KERNEL, "ramoops:%s(%d/%d)",
name, i, *cnt - 1);
prz_ar[i] = persistent_ram_new(*paddr, zone_sz, sig,
&cxt->ecc_info,
cxt->memtype, flags);
&cxt->ecc_info,
cxt->memtype, flags, label);
kfree(label);
if (IS_ERR(prz_ar[i])) {
err = PTR_ERR(prz_ar[i]);
dev_err(dev, "failed to request %s mem region (0x%zx@0x%llx): %d\n",
@ -617,6 +617,7 @@ static int ramoops_init_przs(const char *name,
goto fail;
}
*paddr += zone_sz;
prz_ar[i]->type = pstore_name_to_type(name);
}
*przs = prz_ar;
@ -632,6 +633,8 @@ static int ramoops_init_prz(const char *name,
struct persistent_ram_zone **prz,
phys_addr_t *paddr, size_t sz, u32 sig)
{
char *label;
if (!sz)
return 0;
@ -642,8 +645,10 @@ static int ramoops_init_prz(const char *name,
return -ENOMEM;
}
label = kasprintf(GFP_KERNEL, "ramoops:%s", name);
*prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info,
cxt->memtype, 0);
cxt->memtype, PRZ_FLAG_ZAP_OLD, label);
kfree(label);
if (IS_ERR(*prz)) {
int err = PTR_ERR(*prz);
@ -652,26 +657,31 @@ static int ramoops_init_prz(const char *name,
return err;
}
persistent_ram_zap(*prz);
*paddr += sz;
(*prz)->type = pstore_name_to_type(name);
return 0;
}
static int ramoops_parse_dt_size(struct platform_device *pdev,
const char *propname, u32 *value)
/* Read a u32 from a dt property and make sure it's safe for an int. */
static int ramoops_parse_dt_u32(struct platform_device *pdev,
const char *propname,
u32 default_value, u32 *value)
{
u32 val32 = 0;
int ret;
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",
propname, ret);
return ret;
}
/* Sanity check our results. */
if (val32 > INT_MAX) {
dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32);
return -EOVERFLOW;
@ -685,6 +695,8 @@ static int ramoops_parse_dt(struct platform_device *pdev,
struct ramoops_platform_data *pdata)
{
struct device_node *of_node = pdev->dev.of_node;
struct device_node *parent_node;
struct reserved_mem *rmem;
struct resource *res;
u32 value;
int ret;
@ -693,38 +705,123 @@ static int ramoops_parse_dt(struct platform_device *pdev,
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev,
"failed to locate DT /reserved-memory resource\n");
return -EINVAL;
rmem = of_reserved_mem_lookup(of_node);
if (rmem) {
pdata->mem_size = rmem->size;
pdata->mem_address = rmem->base;
} else {
dev_err(&pdev->dev,
"failed to locate DT /reserved-memory resource\n");
return -EINVAL;
}
} else {
pdata->mem_size = resource_size(res);
pdata->mem_address = res->start;
}
pdata->mem_size = resource_size(res);
pdata->mem_address = res->start;
/*
* Setting "unbuffered" is deprecated and will be ignored if
* "mem_type" is also specified.
*/
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) { \
ret = ramoops_parse_dt_size(pdev, name, &value); \
#define parse_u32(name, field, default_value) { \
ret = ramoops_parse_dt_u32(pdev, name, default_value, \
&value); \
if (ret < 0) \
return ret; \
field = value; \
}
parse_size("record-size", pdata->record_size);
parse_size("console-size", pdata->console_size);
parse_size("ftrace-size", pdata->ftrace_size);
parse_size("pmsg-size", pdata->pmsg_size);
parse_size("ecc-size", pdata->ecc_info.ecc_size);
parse_size("flags", pdata->flags);
parse_u32("mem-type", pdata->mem_type, pdata->mem_type);
parse_u32("record-size", pdata->record_size, 0);
parse_u32("console-size", pdata->console_size, 0);
parse_u32("ftrace-size", pdata->ftrace_size, 0);
parse_u32("pmsg-size", pdata->pmsg_size, 0);
parse_u32("ecc-size", pdata->ecc_info.ecc_size, 0);
parse_u32("flags", pdata->flags, 0);
parse_u32("max-reason", pdata->max_reason, pdata->max_reason);
#ifdef CONFIG_PSTORE_BOOT_LOG
parse_size("boot-log-size", pdata->boot_log_size);
parse_size("boot-log-count", pdata->max_boot_log_cnt);
parse_u32("boot-log-size", pdata->boot_log_size, 0);
parse_u32("boot-log-count", pdata->max_boot_log_cnt, 0);
#endif
#undef parse_size
#undef parse_u32
/*
* Some old Chromebooks relied on the kernel setting the
* console_size and pmsg_size to the record size since that's
* what the downstream kernel did. These same Chromebooks had
* "ramoops" straight under the root node which isn't
* according to the current upstream bindings (though it was
* arguably acceptable under a prior version of the bindings).
* Let's make those old Chromebooks work by detecting that
* we're not a child of "reserved-memory" and mimicking the
* expected behavior.
*/
parent_node = of_get_parent(of_node);
if (!of_node_name_eq(parent_node, "reserved-memory") &&
!pdata->console_size && !pdata->ftrace_size &&
!pdata->pmsg_size && !pdata->ecc_info.ecc_size) {
pdata->console_size = pdata->record_size;
pdata->pmsg_size = pdata->record_size;
}
of_node_put(parent_node);
return 0;
}
#if IS_REACHABLE(CONFIG_ROCKCHIP_MINIDUMP)
static void _ramoops_register_ram_zone_info_to_minidump(struct persistent_ram_zone *prz)
{
struct md_region md_entry = {};
strscpy(md_entry.name, prz->label, sizeof(md_entry.name));
md_entry.virt_addr = (u64)prz->vaddr;
md_entry.phys_addr = prz->paddr;
md_entry.size = prz->size;
if (rk_minidump_add_region(&md_entry) < 0)
pr_err("Failed to add %s in Minidump\n", prz->label);
}
static void ramoops_register_ram_zone_info_to_minidump(struct ramoops_context *cxt)
{
int i = 0;
struct persistent_ram_zone *prz = NULL;
for (i = 0; i < cxt->max_boot_log_cnt; i++) {
prz = cxt->boot_przs[i];
_ramoops_register_ram_zone_info_to_minidump(prz);
}
for (i = 0; i < cxt->max_dump_cnt; i++) {
prz = cxt->dprzs[i];
_ramoops_register_ram_zone_info_to_minidump(prz);
}
for (i = 0; i < cxt->max_ftrace_cnt; i++) {
prz = cxt->fprzs[i];
_ramoops_register_ram_zone_info_to_minidump(prz);
}
prz = cxt->cprz;
_ramoops_register_ram_zone_info_to_minidump(prz);
prz = cxt->mprz;
_ramoops_register_ram_zone_info_to_minidump(prz);
}
#endif
static int ramoops_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -736,15 +833,6 @@ static int ramoops_probe(struct platform_device *pdev)
int err = -EINVAL;
int i = 0;
if (dev_of_node(dev) && !pdata) {
pdata = &pdata_local;
memset(pdata, 0, sizeof(*pdata));
err = ramoops_parse_dt(pdev, pdata);
if (err < 0)
goto fail_out;
}
/*
* Only a single ramoops area allowed at a time, so fail extra
* probes.
@ -754,6 +842,15 @@ static int ramoops_probe(struct platform_device *pdev)
goto fail_out;
}
if (dev_of_node(dev) && !pdata) {
pdata = &pdata_local;
memset(pdata, 0, sizeof(*pdata));
err = ramoops_parse_dt(pdev, pdata);
if (err < 0)
goto fail_out;
}
/* Make sure we didn't get bogus platform data pointer. */
if (!pdata) {
pr_err("NULL platform data\n");
@ -794,7 +891,6 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->console_size = pdata->console_size;
cxt->ftrace_size = pdata->ftrace_size;
cxt->pmsg_size = pdata->pmsg_size;
cxt->dump_oops = pdata->dump_oops;
cxt->flags = pdata->flags;
cxt->ecc_info = pdata->ecc_info;
#ifdef CONFIG_PSTORE_BOOT_LOG
@ -811,7 +907,7 @@ static int ramoops_probe(struct platform_device *pdev)
#endif
#ifdef CONFIG_PSTORE_BOOT_LOG
err = ramoops_init_przs("boot_log", dev, cxt, &cxt->boot_przs, &paddr,
err = ramoops_init_przs("boot-log", dev, cxt, &cxt->boot_przs, &paddr,
cxt->boot_log_size, -1,
&cxt->max_boot_log_cnt, 0, 0);
if (err)
@ -821,7 +917,7 @@ static int ramoops_probe(struct platform_device *pdev)
pr_info("boot-log-%d\t0x%zx@%pa\n", i, cxt->boot_przs[i]->size, &cxt->boot_przs[i]->paddr);
#endif
err = ramoops_init_przs("dump", dev, cxt, &cxt->dprzs, &paddr,
err = ramoops_init_przs("dmesg", dev, cxt, &cxt->dprzs, &paddr,
dump_mem_sz, cxt->record_size,
&cxt->max_dump_cnt, 0, 0);
if (err)
@ -855,7 +951,6 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->pmsg_size, 0);
if (err)
goto fail_init_mprz;
if (cxt->pmsg_size > 0)
pr_info("pmsg\t0x%zx@%pa\n", cxt->mprz->size, &cxt->mprz->paddr);
@ -867,8 +962,10 @@ static int ramoops_probe(struct platform_device *pdev)
* the single region size is how to check.
*/
cxt->pstore.flags = 0;
if (cxt->max_dump_cnt)
if (cxt->max_dump_cnt) {
cxt->pstore.flags |= PSTORE_FLAGS_DMESG;
cxt->pstore.max_reason = pdata->max_reason;
}
if (cxt->console_size)
cxt->pstore.flags |= PSTORE_FLAGS_CONSOLE;
if (cxt->max_ftrace_cnt)
@ -908,14 +1005,16 @@ static int ramoops_probe(struct platform_device *pdev)
mem_size = pdata->mem_size;
mem_address = pdata->mem_address;
record_size = pdata->record_size;
dump_oops = pdata->dump_oops;
ramoops_max_reason = pdata->max_reason;
ramoops_console_size = pdata->console_size;
ramoops_pmsg_size = pdata->pmsg_size;
ramoops_ftrace_size = pdata->ftrace_size;
pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n",
#if IS_REACHABLE(CONFIG_ROCKCHIP_MINIDUMP)
ramoops_register_ram_zone_info_to_minidump(cxt);
#endif
pr_info("using 0x%lx@0x%llx, ecc: %d\n",
cxt->size, (unsigned long long)cxt->phys_addr,
cxt->ecc_info.ecc_size, cxt->ecc_info.block_size);
cxt->ecc_info.ecc_size);
return 0;
@ -967,13 +1066,12 @@ static inline void ramoops_unregister_dummy(void)
{
platform_device_unregister(dummy);
dummy = NULL;
kfree(dummy_data);
dummy_data = NULL;
}
static void __init ramoops_register_dummy(void)
{
struct ramoops_platform_data pdata;
/*
* Prepare a dummy platform data structure to carry the module
* parameters. If mem_size isn't set, then there are no module
@ -984,35 +1082,38 @@ static void __init ramoops_register_dummy(void)
pr_info("using module parameters\n");
dummy_data = kzalloc(sizeof(*dummy_data), GFP_KERNEL);
if (!dummy_data) {
pr_info("could not allocate pdata\n");
return;
}
dummy_data->mem_size = mem_size;
dummy_data->mem_address = mem_address;
dummy_data->mem_type = mem_type;
dummy_data->record_size = record_size;
dummy_data->console_size = ramoops_console_size;
dummy_data->ftrace_size = ramoops_ftrace_size;
dummy_data->pmsg_size = ramoops_pmsg_size;
dummy_data->dump_oops = dump_oops;
dummy_data->flags = RAMOOPS_FLAG_FTRACE_PER_CPU;
memset(&pdata, 0, sizeof(pdata));
pdata.mem_size = mem_size;
pdata.mem_address = mem_address;
pdata.mem_type = mem_type;
pdata.record_size = record_size;
pdata.console_size = ramoops_console_size;
pdata.ftrace_size = ramoops_ftrace_size;
pdata.pmsg_size = ramoops_pmsg_size;
/* 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;
/*
* For backwards compatibility ramoops.ecc=1 means 16 bytes ECC
* (using 1 byte for ECC isn't much of use anyway).
*/
dummy_data->ecc_info.ecc_size = ramoops_ecc == 1 ? 16 : ramoops_ecc;
pdata.ecc_info.ecc_size = ramoops_ecc == 1 ? 16 : ramoops_ecc;
dummy = platform_device_register_data(NULL, "ramoops", -1,
dummy_data, sizeof(struct ramoops_platform_data));
&pdata, sizeof(pdata));
if (IS_ERR(dummy)) {
pr_info("could not create platform device: %ld\n",
PTR_ERR(dummy));
dummy = NULL;
ramoops_unregister_dummy();
}
}

View File

@ -1,18 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "persistent_ram: " fmt
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
#include <linux/err.h>
@ -29,6 +20,23 @@
#include <linux/vmalloc.h>
#include <asm/page.h>
/**
* struct persistent_ram_buffer - persistent circular RAM buffer
*
* @sig:
* signature to indicate header (PERSISTENT_RAM_SIG xor PRZ-type value)
* @start:
* offset into @data where the beginning of the stored bytes begin
* @size:
* number of valid bytes stored in @data
*/
struct persistent_ram_buffer {
uint32_t sig;
atomic_t start;
atomic_t size;
uint8_t data[];
};
#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
static inline size_t buffer_size(struct persistent_ram_zone *prz)
@ -275,7 +283,7 @@ static int notrace persistent_ram_update_user(struct persistent_ram_zone *prz,
const void __user *s, unsigned int start, unsigned int count)
{
struct persistent_ram_buffer *buffer = prz->buffer;
int ret = unlikely(__copy_from_user(buffer->data + start, s, count)) ?
int ret = unlikely(copy_from_user(buffer->data + start, s, count)) ?
-EFAULT : 0;
persistent_ram_update_ecc(prz, start, count);
return ret;
@ -340,8 +348,6 @@ int notrace persistent_ram_write_user(struct persistent_ram_zone *prz,
int rem, ret = 0, c = count;
size_t start;
if (unlikely(!access_ok(VERIFY_READ, s, count)))
return -EFAULT;
if (unlikely(c > prz->buffer_size)) {
s += c - prz->buffer_size;
c = prz->buffer_size;
@ -390,6 +396,10 @@ void persistent_ram_zap(struct persistent_ram_zone *prz)
persistent_ram_update_header_ecc(prz);
}
#define MEM_TYPE_WCOMBINE 0
#define MEM_TYPE_NONCACHED 1
#define MEM_TYPE_NORMAL 2
static void *persistent_ram_vmap(phys_addr_t start, size_t size,
unsigned int memtype)
{
@ -403,10 +413,20 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size,
page_start = start - offset_in_page(start);
page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
if (memtype)
switch (memtype) {
case MEM_TYPE_NORMAL:
prot = PAGE_KERNEL;
break;
case MEM_TYPE_NONCACHED:
prot = pgprot_noncached(PAGE_KERNEL);
else
break;
case MEM_TYPE_WCOMBINE:
prot = pgprot_writecombine(PAGE_KERNEL);
break;
default:
pr_err("invalid mem_type=%d\n", memtype);
return NULL;
}
pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL);
if (!pages) {
@ -431,12 +451,13 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size,
}
static void *persistent_ram_iomap(phys_addr_t start, size_t size,
unsigned int memtype)
unsigned int memtype, char *label)
{
void *va;
if (!request_mem_region(start, size, "persistent_ram")) {
pr_err("request mem region (0x%llx@0x%llx) failed\n",
if (!request_mem_region(start, size, label ?: "ramoops")) {
pr_err("request mem region (%s 0x%llx@0x%llx) failed\n",
label ?: "ramoops",
(unsigned long long)size, (unsigned long long)start);
return NULL;
}
@ -463,7 +484,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
if (pfn_valid(start >> PAGE_SHIFT))
prz->vaddr = persistent_ram_vmap(start, size, memtype);
else
prz->vaddr = persistent_ram_iomap(start, size, memtype);
prz->vaddr = persistent_ram_iomap(start, size, memtype,
prz->label);
if (!prz->vaddr) {
pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__,
@ -481,10 +503,13 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
struct persistent_ram_ecc_info *ecc_info)
{
int ret;
bool zap = !!(prz->flags & PRZ_FLAG_ZAP_OLD);
ret = persistent_ram_init_ecc(prz, ecc_info);
if (ret)
if (ret) {
pr_warn("ECC failed %s\n", prz->label);
return ret;
}
sig ^= PERSISTENT_RAM_SIG;
@ -495,23 +520,25 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
}
if (buffer_size(prz) > prz->buffer_size ||
buffer_start(prz) > buffer_size(prz))
buffer_start(prz) > buffer_size(prz)) {
pr_info("found existing invalid buffer, size %zu, start %zu\n",
buffer_size(prz), buffer_start(prz));
else {
zap = true;
} else {
pr_debug("found existing buffer, size %zu, start %zu\n",
buffer_size(prz), buffer_start(prz));
persistent_ram_save_old(prz);
return 0;
}
} else {
pr_debug("no valid data in buffer (sig = 0x%08x)\n",
prz->buffer->sig);
prz->buffer->sig = sig;
zap = true;
}
/* Rewind missing or invalid memory area. */
prz->buffer->sig = sig;
persistent_ram_zap(prz);
/* Reset missing, invalid, or single-use memory area. */
if (zap)
persistent_ram_zap(prz);
return 0;
}
@ -539,12 +566,13 @@ void persistent_ram_free(struct persistent_ram_zone *prz)
prz->ecc_info.par = NULL;
persistent_ram_free_old(prz);
kfree(prz->label);
kfree(prz);
}
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
unsigned int memtype, u32 flags)
unsigned int memtype, u32 flags, char *label)
{
struct persistent_ram_zone *prz;
int ret = -ENOMEM;
@ -558,6 +586,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
/* Initialize general buffer state. */
raw_spin_lock_init(&prz->buffer_lock);
prz->flags = flags;
prz->label = kstrdup(label, GFP_KERNEL);
ret = persistent_ram_buffer_map(start, size, prz, memtype);
if (ret)
@ -567,6 +596,12 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
if (ret)
goto err;
pr_debug("attached %s 0x%zx@0x%llx: %zu header, %zu data, %zu ecc (%d/%d)\n",
prz->label, prz->size, (unsigned long long)prz->paddr,
sizeof(*prz->buffer), prz->buffer_size,
prz->size - sizeof(*prz->buffer) - prz->buffer_size,
prz->ecc_info.ecc_size, prz->ecc_info.block_size);
return prz;
err:
persistent_ram_free(prz);

1464
fs/pstore/zone.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@ enum kmsg_dump_reason {
KMSG_DUMP_RESTART,
KMSG_DUMP_HALT,
KMSG_DUMP_POWEROFF,
KMSG_DUMP_SHUTDOWN = KMSG_DUMP_POWEROFF,
};
/**
@ -54,7 +55,7 @@ struct kmsg_dumper {
#ifdef CONFIG_PRINTK
void kmsg_dump(enum kmsg_dump_reason reason);
const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason);
bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
char *line, size_t size, size_t *len);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Persistent Storage - pstore.h
*
@ -5,19 +6,6 @@
*
* This code is the generic layer to export data records from platform
* level persistent storage via a file system.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _LINUX_PSTORE_H
#define _LINUX_PSTORE_H
@ -32,13 +20,19 @@
struct module;
/* pstore record types (see fs/pstore/inode.c for filename templates) */
/*
* pstore record types (see fs/pstore/platform.c for pstore_type_names[])
* These values may be written to storage (see EFI vars backend), so
* they are kind of an ABI. Be careful changing the mappings.
*/
enum pstore_type_id {
/* Frontend storage types */
PSTORE_TYPE_DMESG = 0,
PSTORE_TYPE_MCE = 1,
PSTORE_TYPE_CONSOLE = 2,
PSTORE_TYPE_FTRACE = 3,
/* PPC64 partition types */
/* PPC64-specific partition types */
PSTORE_TYPE_PPC_RTAS = 4,
PSTORE_TYPE_PPC_OF = 5,
PSTORE_TYPE_PPC_COMMON = 6,
@ -47,9 +41,14 @@ enum pstore_type_id {
#ifdef CONFIG_PSTORE_BOOT_LOG
PSTORE_TYPE_BOOT_LOG = 9,
#endif
PSTORE_TYPE_UNKNOWN = 255
/* End of the list */
PSTORE_TYPE_MAX
};
const char *pstore_type_to_name(enum pstore_type_id type);
enum pstore_type_id pstore_name_to_type(const char *name);
struct pstore_info;
/**
* struct pstore_record - details of a pstore record entry
@ -88,7 +87,7 @@ struct pstore_record {
/**
* struct pstore_info - backend pstore driver structure
*
* @owner: module which is repsonsible for this backend driver
* @owner: module which is responsible for this backend driver
* @name: name of the backend driver
*
* @buf_lock: semaphore to serialize access to @buf
@ -100,6 +99,12 @@ struct pstore_record {
*
* @read_mutex: serializes @open, @read, @close, and @erase callbacks
* @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
*
* Callbacks:
@ -174,7 +179,7 @@ struct pstore_record {
*/
struct pstore_info {
struct module *owner;
char *name;
const char *name;
struct semaphore buf_lock;
char *buf;
@ -183,6 +188,7 @@ struct pstore_info {
struct mutex read_mutex;
int flags;
int max_reason;
void *data;
int (*open)(struct pstore_info *psi);
@ -195,13 +201,14 @@ struct pstore_info {
};
/* Supported frontends */
#define PSTORE_FLAGS_DMESG (1 << 0)
#define PSTORE_FLAGS_CONSOLE (1 << 1)
#define PSTORE_FLAGS_FTRACE (1 << 2)
#define PSTORE_FLAGS_PMSG (1 << 3)
#define PSTORE_FLAGS_DMESG BIT(0)
#define PSTORE_FLAGS_CONSOLE BIT(1)
#define PSTORE_FLAGS_FTRACE BIT(2)
#define PSTORE_FLAGS_PMSG BIT(3)
#ifdef CONFIG_PSTORE_BOOT_LOG
#define PSTORE_FLAGS_BOOT_LOG (1 << 4)
#define PSTORE_FLAGS_BOOT_LOG BIT(4)
#endif
extern int pstore_register(struct pstore_info *);
extern void pstore_unregister(struct pstore_info *);

View File

@ -1,17 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
* Copyright (C) 2011 Kees Cook <keescook@chromium.org>
* Copyright (C) 2011 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __LINUX_PSTORE_RAM_H__
@ -22,8 +13,8 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/pstore.h>
#include <linux/types.h>
/*
* Choose whether access to the RAM zone requires locking or not. If a zone
@ -31,17 +22,15 @@
* PRZ_FLAG_NO_LOCK is used. For all other cases, locking is required.
*/
#define PRZ_FLAG_NO_LOCK BIT(0)
/*
* If a PRZ should only have a single-boot lifetime, this marks it as
* getting wiped after its contents get copied out after boot.
*/
#define PRZ_FLAG_ZAP_OLD BIT(1)
struct persistent_ram_buffer;
struct rs_control;
struct persistent_ram_buffer {
uint32_t sig;
atomic_t start;
atomic_t size;
uint8_t data[0];
};
struct persistent_ram_ecc_info {
int block_size;
int ecc_size;
@ -50,16 +39,55 @@ struct persistent_ram_ecc_info {
uint16_t *par;
};
/**
* struct persistent_ram_zone - Details of a persistent RAM zone (PRZ)
* used as a pstore backend
*
* @paddr: physical address of the mapped RAM area
* @size: size of mapping
* @label: unique name of this PRZ
* @type: frontend type for this PRZ
* @flags: holds PRZ_FLAGS_* bits
*
* @buffer_lock:
* locks access to @buffer "size" bytes and "start" offset
* @buffer:
* pointer to actual RAM area managed by this PRZ
* @buffer_size:
* bytes in @buffer->data (not including any trailing ECC bytes)
*
* @par_buffer:
* pointer into @buffer->data containing ECC bytes for @buffer->data
* @par_header:
* pointer into @buffer->data containing ECC bytes for @buffer header
* (i.e. all fields up to @data)
* @rs_decoder:
* RSLIB instance for doing ECC calculations
* @corrected_bytes:
* ECC corrected bytes accounting since boot
* @bad_blocks:
* ECC uncorrectable bytes accounting since boot
* @ecc_info:
* ECC configuration details
*
* @old_log:
* saved copy of @buffer->data prior to most recent wipe
* @old_log_size:
* bytes contained in @old_log
*
*/
struct persistent_ram_zone {
phys_addr_t paddr;
size_t size;
void *vaddr;
char *label;
enum pstore_type_id type;
u32 flags;
raw_spinlock_t buffer_lock;
struct persistent_ram_buffer *buffer;
size_t buffer_size;
u32 flags;
raw_spinlock_t buffer_lock;
/* ECC correction */
char *par_buffer;
char *par_header;
struct rs_control *rs_decoder;
@ -71,45 +99,9 @@ struct persistent_ram_zone {
size_t old_log_size;
};
struct ramoops_context {
struct persistent_ram_zone **dprzs; /* Oops dump zones */
struct persistent_ram_zone *cprz; /* Console zone */
struct persistent_ram_zone **fprzs; /* Ftrace zones */
struct persistent_ram_zone *mprz; /* PMSG zone */
#ifdef CONFIG_PSTORE_BOOT_LOG
struct persistent_ram_zone **boot_przs; /* BOOT log zones */
#endif
phys_addr_t phys_addr;
unsigned long size;
unsigned int memtype;
size_t record_size;
size_t console_size;
size_t ftrace_size;
size_t pmsg_size;
#ifdef CONFIG_PSTORE_BOOT_LOG
size_t boot_log_size;
#endif
int dump_oops;
u32 flags;
struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt;
unsigned int dump_write_cnt;
/* _read_cnt need clear on ramoops_pstore_open */
unsigned int dump_read_cnt;
unsigned int console_read_cnt;
unsigned int max_ftrace_cnt;
unsigned int ftrace_read_cnt;
unsigned int pmsg_read_cnt;
#ifdef CONFIG_PSTORE_BOOT_LOG
unsigned int boot_log_read_cnt;
unsigned int max_boot_log_cnt;
#endif
struct pstore_info pstore;
};
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
unsigned int memtype, u32 flags);
unsigned int memtype, u32 flags, char *label);
void persistent_ram_free(struct persistent_ram_zone *prz);
void persistent_ram_zap(struct persistent_ram_zone *prz);
@ -124,6 +116,9 @@ void *persistent_ram_old(struct persistent_ram_zone *prz);
void persistent_ram_free_old(struct persistent_ram_zone *prz);
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
char *str, size_t len);
#ifdef CONFIG_PSTORE_BOOT_LOG
ssize_t ramoops_pstore_read_for_boot_log(struct pstore_record *record);
#endif
/*
* Ramoops platform data
@ -145,7 +140,7 @@ struct ramoops_platform_data {
unsigned long boot_log_size;
unsigned long max_boot_log_cnt;
#endif
int dump_oops;
int max_reason;
u32 flags;
struct persistent_ram_ecc_info ecc_info;
};

View File

@ -3260,6 +3260,23 @@ void kmsg_dump(enum kmsg_dump_reason reason)
rcu_read_unlock();
}
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_get_line_nolock - retrieve one kmsg log line (unlocked version)
* @dumper: registered kmsg dumper