pstore/blk: Support non-block storage devices

Add support for non-block devices (e.g. MTD). A non-block driver calls
pstore_blk_register_device() to register iself.

In addition, pstore/zone is updated to handle non-block devices,
where an erase must be done before a write. Without this, there is no
way to remove records stored to an MTD.

Signed-off-by: WeiXiong Liao <liaoweixiong@allwinnertech.com>
Link: https://lore.kernel.org/lkml/20200511233229.27745-10-keescook@chromium.org/
Co-developed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
WeiXiong Liao 2020-03-25 16:55:05 +08:00 committed by Kees Cook
parent 1525fb3bb6
commit 7dcb7848ba
5 changed files with 115 additions and 49 deletions

View File

@ -7,8 +7,8 @@ Introduction
------------
pstore block (pstore/blk) is an oops/panic logger that writes its logs to a
block device before the system crashes. You can get these log files by
mounting pstore filesystem like::
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
@ -24,8 +24,8 @@ 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, such as total_size
of block device and read/write operations.
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
-----------------------
@ -152,6 +152,15 @@ 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
----------------------

View File

@ -105,55 +105,22 @@ struct bdev_info {
_##name_; \
})
/**
* 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.
* @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_write_op panic_write;
};
static int psblk_register_do(struct pstore_device_info *dev)
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;
mutex_lock(&pstore_blk_lock);
/* someone already registered before */
if (pstore_zone_info) {
mutex_unlock(&pstore_blk_lock);
if (pstore_zone_info)
return -EBUSY;
}
pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
if (!pstore_zone_info) {
mutex_unlock(&pstore_blk_lock);
if (!pstore_zone_info)
return -ENOMEM;
}
/* zero means not limit on which backends to attempt to store. */
if (!dev->flags)
@ -179,6 +146,7 @@ static int psblk_register_do(struct pstore_device_info *dev)
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;
@ -188,20 +156,51 @@ static int psblk_register_do(struct pstore_device_info *dev)
kfree(pstore_zone_info);
pstore_zone_info = NULL;
}
mutex_unlock(&pstore_blk_lock);
return ret;
}
static void psblk_unregister_do(struct pstore_device_info *dev)
/**
* 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
@ -396,9 +395,10 @@ static int __register_pstore_blk(struct pstore_blk_info *info)
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 = psblk_register_do(&dev);
ret = __register_pstore_device(&dev);
if (ret)
goto err_put_bdev;
@ -442,7 +442,7 @@ static void __unregister_pstore_blk(unsigned int major)
lockdep_assert_held(&pstore_blk_lock);
if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
psblk_unregister_do(&dev);
__unregister_pstore_device(&dev);
psblk_put_bdev(psblk_bdev, holder);
blkdev_panic_write = NULL;
psblk_bdev = NULL;
@ -481,6 +481,13 @@ 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);

View File

@ -660,15 +660,21 @@ static inline int psz_kmsg_erase(struct psz_context *cxt,
struct psz_buffer *buffer = zone->buffer;
struct psz_kmsg_header *hdr =
(struct psz_kmsg_header *)buffer->data;
size_t size;
if (unlikely(!psz_ok(zone)))
return 0;
/* this zone is already updated, no need to erase */
if (record->count != hdr->counter)
return 0;
size = buffer_datalen(zone) + sizeof(*zone->buffer);
atomic_set(&zone->buffer->datalen, 0);
return psz_zone_write(zone, FLUSH_META, NULL, 0, 0);
if (cxt->pstore_zone_info->erase)
return cxt->pstore_zone_info->erase(size, zone->off);
else
return psz_zone_write(zone, FLUSH_META, NULL, 0, 0);
}
static inline int psz_record_erase(struct psz_context *cxt,

View File

@ -49,6 +49,44 @@ struct pstore_blk_info {
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
*

View File

@ -7,6 +7,7 @@
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
*
@ -27,6 +28,10 @@ typedef ssize_t (*pstore_zone_write_op)(const char *, size_t, loff_t);
* @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.
@ -45,6 +50,7 @@ struct pstore_zone_info {
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;
};