Commit 7dcb7848 authored by WeiXiong Liao's avatar WeiXiong Liao Committed by Kees Cook

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: default avatarWeiXiong Liao <liaoweixiong@allwinnertech.com>
Link: https://lore.kernel.org/lkml/20200511233229.27745-10-keescook@chromium.org/Co-developed-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
parent 1525fb3b
...@@ -7,8 +7,8 @@ Introduction ...@@ -7,8 +7,8 @@ Introduction
------------ ------------
pstore block (pstore/blk) is an oops/panic logger that writes its logs to a 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 block device and non-block device before the system crashes. You can get
mounting pstore filesystem like:: these log files by mounting pstore filesystem like::
mount -t pstore pstore /sys/fs/pstore mount -t pstore pstore /sys/fs/pstore
...@@ -24,8 +24,8 @@ Configurations for user determine how pstore/blk works, such as pmsg_size, ...@@ -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, kmsg_size and so on. All of them support both Kconfig and module parameters,
but module parameters have priority over Kconfig. but module parameters have priority over Kconfig.
Configurations for driver are all about block device, such as total_size Configurations for driver are all about block device and non-block device,
of block device and read/write operations. such as total_size of block device and read/write operations.
Configurations for user Configurations for user
----------------------- -----------------------
...@@ -152,6 +152,15 @@ driver uses ``register_pstore_blk`` to register to pstore/blk. ...@@ -152,6 +152,15 @@ driver uses ``register_pstore_blk`` to register to pstore/blk.
.. kernel-doc:: fs/pstore/blk.c .. kernel-doc:: fs/pstore/blk.c
:identifiers: register_pstore_blk :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 Compression and header
---------------------- ----------------------
......
...@@ -105,55 +105,22 @@ struct bdev_info { ...@@ -105,55 +105,22 @@ struct bdev_info {
_##name_; \ _##name_; \
}) })
/** static int __register_pstore_device(struct pstore_device_info *dev)
* 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)
{ {
int ret; int ret;
lockdep_assert_held(&pstore_blk_lock);
if (!dev || !dev->total_size || !dev->read || !dev->write) if (!dev || !dev->total_size || !dev->read || !dev->write)
return -EINVAL; return -EINVAL;
mutex_lock(&pstore_blk_lock);
/* someone already registered before */ /* someone already registered before */
if (pstore_zone_info) { if (pstore_zone_info)
mutex_unlock(&pstore_blk_lock);
return -EBUSY; return -EBUSY;
}
pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL); pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
if (!pstore_zone_info) { if (!pstore_zone_info)
mutex_unlock(&pstore_blk_lock);
return -ENOMEM; return -ENOMEM;
}
/* zero means not limit on which backends to attempt to store. */ /* zero means not limit on which backends to attempt to store. */
if (!dev->flags) if (!dev->flags)
...@@ -179,6 +146,7 @@ static int psblk_register_do(struct pstore_device_info *dev) ...@@ -179,6 +146,7 @@ static int psblk_register_do(struct pstore_device_info *dev)
pstore_zone_info->max_reason = max_reason; pstore_zone_info->max_reason = max_reason;
pstore_zone_info->read = dev->read; pstore_zone_info->read = dev->read;
pstore_zone_info->write = dev->write; pstore_zone_info->write = dev->write;
pstore_zone_info->erase = dev->erase;
pstore_zone_info->panic_write = dev->panic_write; pstore_zone_info->panic_write = dev->panic_write;
pstore_zone_info->name = KBUILD_MODNAME; pstore_zone_info->name = KBUILD_MODNAME;
pstore_zone_info->owner = THIS_MODULE; pstore_zone_info->owner = THIS_MODULE;
...@@ -188,20 +156,51 @@ static int psblk_register_do(struct pstore_device_info *dev) ...@@ -188,20 +156,51 @@ static int psblk_register_do(struct pstore_device_info *dev)
kfree(pstore_zone_info); kfree(pstore_zone_info);
pstore_zone_info = NULL; 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); mutex_unlock(&pstore_blk_lock);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(register_pstore_device);
static void psblk_unregister_do(struct pstore_device_info *dev) static void __unregister_pstore_device(struct pstore_device_info *dev)
{ {
mutex_lock(&pstore_blk_lock); lockdep_assert_held(&pstore_blk_lock);
if (pstore_zone_info && pstore_zone_info->read == dev->read) { if (pstore_zone_info && pstore_zone_info->read == dev->read) {
unregister_pstore_zone(pstore_zone_info); unregister_pstore_zone(pstore_zone_info);
kfree(pstore_zone_info); kfree(pstore_zone_info);
pstore_zone_info = NULL; 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); mutex_unlock(&pstore_blk_lock);
} }
EXPORT_SYMBOL_GPL(unregister_pstore_device);
/** /**
* psblk_get_bdev() - open block device * psblk_get_bdev() - open block device
...@@ -396,9 +395,10 @@ static int __register_pstore_blk(struct pstore_blk_info *info) ...@@ -396,9 +395,10 @@ static int __register_pstore_blk(struct pstore_blk_info *info)
dev.flags = info->flags; dev.flags = info->flags;
dev.read = psblk_generic_blk_read; dev.read = psblk_generic_blk_read;
dev.write = psblk_generic_blk_write; dev.write = psblk_generic_blk_write;
dev.erase = NULL;
dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL; dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
ret = psblk_register_do(&dev); ret = __register_pstore_device(&dev);
if (ret) if (ret)
goto err_put_bdev; goto err_put_bdev;
...@@ -442,7 +442,7 @@ static void __unregister_pstore_blk(unsigned int major) ...@@ -442,7 +442,7 @@ static void __unregister_pstore_blk(unsigned int major)
lockdep_assert_held(&pstore_blk_lock); lockdep_assert_held(&pstore_blk_lock);
if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) { if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
psblk_unregister_do(&dev); __unregister_pstore_device(&dev);
psblk_put_bdev(psblk_bdev, holder); psblk_put_bdev(psblk_bdev, holder);
blkdev_panic_write = NULL; blkdev_panic_write = NULL;
psblk_bdev = NULL; psblk_bdev = NULL;
...@@ -481,6 +481,13 @@ static void __exit pstore_blk_exit(void) ...@@ -481,6 +481,13 @@ static void __exit pstore_blk_exit(void)
mutex_lock(&pstore_blk_lock); mutex_lock(&pstore_blk_lock);
if (psblk_bdev) if (psblk_bdev)
__unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev)); __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); mutex_unlock(&pstore_blk_lock);
} }
module_exit(pstore_blk_exit); module_exit(pstore_blk_exit);
......
...@@ -660,15 +660,21 @@ static inline int psz_kmsg_erase(struct psz_context *cxt, ...@@ -660,15 +660,21 @@ static inline int psz_kmsg_erase(struct psz_context *cxt,
struct psz_buffer *buffer = zone->buffer; struct psz_buffer *buffer = zone->buffer;
struct psz_kmsg_header *hdr = struct psz_kmsg_header *hdr =
(struct psz_kmsg_header *)buffer->data; (struct psz_kmsg_header *)buffer->data;
size_t size;
if (unlikely(!psz_ok(zone))) if (unlikely(!psz_ok(zone)))
return 0; return 0;
/* this zone is already updated, no need to erase */ /* this zone is already updated, no need to erase */
if (record->count != hdr->counter) if (record->count != hdr->counter)
return 0; return 0;
size = buffer_datalen(zone) + sizeof(*zone->buffer);
atomic_set(&zone->buffer->datalen, 0); 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, static inline int psz_record_erase(struct psz_context *cxt,
......
...@@ -49,6 +49,44 @@ struct pstore_blk_info { ...@@ -49,6 +49,44 @@ struct pstore_blk_info {
int register_pstore_blk(struct pstore_blk_info *info); int register_pstore_blk(struct pstore_blk_info *info);
void unregister_pstore_blk(unsigned int major); 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 * struct pstore_blk_config - the pstore_blk backend configuration
* *
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
typedef ssize_t (*pstore_zone_read_op)(char *, size_t, loff_t); 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_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 * 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); ...@@ -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: * @write: The same as @read, but the following error number:
* -EBUSY means try to write again later. * -EBUSY means try to write again later.
* -ENOMSG means to try next zone. * -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 * @panic_write:The write operation only used for panic case. It's optional
* if you do not care panic log. The parameters are relative * if you do not care panic log. The parameters are relative
* value to storage. * value to storage.
...@@ -45,6 +50,7 @@ struct pstore_zone_info { ...@@ -45,6 +50,7 @@ struct pstore_zone_info {
unsigned long ftrace_size; unsigned long ftrace_size;
pstore_zone_read_op read; pstore_zone_read_op read;
pstore_zone_write_op write; pstore_zone_write_op write;
pstore_zone_erase_op erase;
pstore_zone_write_op panic_write; pstore_zone_write_op panic_write;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment