Skip to content

Commit

Permalink
pstore/blk: Support non-block storage devices
Browse files Browse the repository at this point in the history
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 <[email protected]>
Link: https://lore.kernel.org/lkml/[email protected]/
Co-developed-by: Kees Cook <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
  • Loading branch information
WeiXiong Liao authored and kees committed Jun 1, 2020
1 parent 1525fb3 commit 7dcb784
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 48 deletions.
17 changes: 13 additions & 4 deletions Documentation/admin-guide/pstore-blk.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
-----------------------
Expand Down Expand Up @@ -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
----------------------

Expand Down
93 changes: 50 additions & 43 deletions fs/pstore/blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -188,20 +156,51 @@ static int psblk_register_do(struct pstore_device_info *dev)
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 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) {
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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion fs/pstore/zone.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
38 changes: 38 additions & 0 deletions include/linux/pstore_blk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
6 changes: 6 additions & 0 deletions include/linux/pstore_zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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.
Expand All @@ -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;
};

Expand Down

0 comments on commit 7dcb784

Please sign in to comment.