2020-01-16 10:24:04

by WeiXiong Liao

[permalink] [raw]
Subject: [PATCH 00/11] pstore: support crash log to block and mtd device

Why should we need to log to block (mtd) device?
1. Most embedded intelligent equipment have no persistent ram, which
increases costs. We perfer to cheaper solutions, like block devices.
2. Do not any equipment have battery, which means that it lost all data
on general ram if power failure. Pstore has little to do for these
equipments.

Why should we adapt pstore/blk to MTD device instead of mtdoops?
1. repetitive jobs between pstore and mtdoops
Both of pstore and mtdoops do the same jobs that store panic/oops log.
2. do what a driver should do
To me, a driver should provide methods instead of policies. What MTD
should do is to provide read/write/erase operations, geting rid of codes
about chunk management, kmsg dumper and configuration.
3. enhanced feature
Not only store log, but also show it as files.
Not only log, but also trigger time and trigger count.
Not only panic/oops log, but also log recorder for pmsg, console and
ftrace in the future.

Before upstream submission, pstore/blk is tested on arch ARM and x84_64,
block device and mtd device, built as modules and in kernel. Here are the
details:

https://github.com/gmpy/articles/blob/master/pstore/Test-Pstore-Block.md

WeiXiong Liao (11):
pstore/blk: new support logger for block devices
blkoops: add blkoops, a warpper for pstore/blk
pstore/blk: support pmsg recorder
pstore/blk: blkoops: support console recorder
pstore/blk: blkoops: support ftrace recorder
Documentation: pstore/blk: blkoops: create document for pstore_blk
pstore/blk: skip broken zone for mtd device
blkoops: respect for device to pick recorders
pstore/blk: blkoops: support special removing jobs for dmesg.
blkoops: add interface for dirver to get information of blkoops
mtd: new support oops logger based on pstore/blk

Documentation/admin-guide/pstore-block.rst | 297 ++++++
MAINTAINERS | 3 +-
drivers/mtd/Kconfig | 10 +
drivers/mtd/Makefile | 1 +
drivers/mtd/mtdpstore.c | 531 +++++++++++
fs/pstore/Kconfig | 109 +++
fs/pstore/Makefile | 5 +
fs/pstore/blkoops.c | 490 ++++++++++
fs/pstore/blkzone.c | 1341 ++++++++++++++++++++++++++++
include/linux/blkoops.h | 94 ++
include/linux/pstore_blk.h | 91 ++
11 files changed, 2971 insertions(+), 1 deletion(-)
create mode 100644 Documentation/admin-guide/pstore-block.rst
create mode 100644 drivers/mtd/mtdpstore.c
create mode 100644 fs/pstore/blkoops.c
create mode 100644 fs/pstore/blkzone.c
create mode 100644 include/linux/blkoops.h
create mode 100644 include/linux/pstore_blk.h

--
1.9.1


2020-01-16 10:24:12

by WeiXiong Liao

[permalink] [raw]
Subject: [PATCH 07/11] pstore/blk: skip broken zone for mtd device

It's one of a series of patches for adaptive to MTD device.

MTD device is not block device. As the block of flash (MTD device) will
be broken, it's necessary for pstore/blk to skip the broken block
(bad block).

If device drivers return -ENEXT, pstore/blk will try next zone of dmesg.

Signed-off-by: WeiXiong Liao <[email protected]>
---
Documentation/admin-guide/pstore-block.rst | 3 +-
fs/pstore/blkzone.c | 74 +++++++++++++++++++++++-------
include/linux/blkoops.h | 4 +-
include/linux/pstore_blk.h | 4 ++
4 files changed, 66 insertions(+), 19 deletions(-)

diff --git a/Documentation/admin-guide/pstore-block.rst b/Documentation/admin-guide/pstore-block.rst
index 58418d429c55..aea6d2664a22 100644
--- a/Documentation/admin-guide/pstore-block.rst
+++ b/Documentation/admin-guide/pstore-block.rst
@@ -185,7 +185,8 @@ The parameter @offset is the relative position of the device.
Normally the number of bytes read/written should be returned, while for error,
negative number will be returned. The following return numbers mean more:

--EBUSY: pstore/blk should try again later.
+1. -EBUSY: pstore/blk should try again later.
+#. -ENEXT: this zone is used or broken, pstore/blk should try next one.

panic_write (for non-block device)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/fs/pstore/blkzone.c b/fs/pstore/blkzone.c
index a14b1b3d9053..7c6bf14f7e7d 100644
--- a/fs/pstore/blkzone.c
+++ b/fs/pstore/blkzone.c
@@ -222,6 +222,9 @@ static int blkz_zone_write(struct blkz_zone *zone,

return 0;
set_dirty:
+ /* no need to mark dirty if going to try next zone */
+ if (wcnt == -ENEXT)
+ return -ENEXT;
atomic_set(&zone->dirty, true);
/* flush dirty zones nicely */
if (wcnt == -EBUSY && !is_on_panic())
@@ -375,7 +378,11 @@ static int blkz_recover_dmesg_meta(struct blkz_context *cxt)
return -EINVAL;

rcnt = info->read((char *)buf, len, zone->off);
- if (rcnt != len) {
+ if (rcnt == -ENEXT) {
+ pr_debug("%s with id %lu may be broken, skip\n",
+ zone->name, i);
+ continue;
+ } else if (rcnt != len) {
pr_err("read %s with id %lu failed\n", zone->name, i);
return (int)rcnt < 0 ? (int)rcnt : -EIO;
}
@@ -665,24 +672,58 @@ static void blkz_write_kmsg_hdr(struct blkz_zone *zone,
hdr->counter = 0;
}

+/*
+ * In case zone is broken, which may occur to MTD device, we try each zones,
+ * start at cxt->dmesg_write_cnt.
+ */
static inline int notrace blkz_dmesg_write_do(struct blkz_context *cxt,
struct pstore_record *record)
{
+ int ret = -EBUSY;
size_t size, hlen;
struct blkz_zone *zone;
- unsigned int zonenum;
+ unsigned int i;

- zonenum = cxt->dmesg_write_cnt;
- zone = cxt->dbzs[zonenum];
- if (unlikely(!zone))
- return -ENOSPC;
- cxt->dmesg_write_cnt = (zonenum + 1) % cxt->dmesg_max_cnt;
+ for (i = 0; i < cxt->dmesg_max_cnt; i++) {
+ unsigned int zonenum, len;
+
+ zonenum = (cxt->dmesg_write_cnt + i) % cxt->dmesg_max_cnt;
+ zone = cxt->dbzs[zonenum];
+ if (unlikely(!zone))
+ return -ENOSPC;

- pr_debug("write %s to zone id %d\n", zone->name, zonenum);
- blkz_write_kmsg_hdr(zone, record);
- hlen = sizeof(struct blkz_dmesg_header);
- size = min_t(size_t, record->size, zone->buffer_size - hlen);
- return blkz_zone_write(zone, FLUSH_ALL, record->buf, size, hlen);
+ /* avoid destorying old data, allocate a new one */
+ len = zone->buffer_size + sizeof(*zone->buffer);
+ zone->oldbuf = zone->buffer;
+ zone->buffer = kzalloc(len, GFP_KERNEL);
+ if (!zone->buffer) {
+ zone->buffer = zone->oldbuf;
+ return -ENOMEM;
+ }
+ zone->buffer->sig = zone->oldbuf->sig;
+
+ pr_debug("write %s to zone id %d\n", zone->name, zonenum);
+ blkz_write_kmsg_hdr(zone, record);
+ hlen = sizeof(struct blkz_dmesg_header);
+ size = min_t(size_t, record->size, zone->buffer_size - hlen);
+ ret = blkz_zone_write(zone, FLUSH_ALL, record->buf, size, hlen);
+ if (likely(!ret || ret != -ENEXT)) {
+ cxt->dmesg_write_cnt = zonenum + 1;
+ cxt->dmesg_write_cnt %= cxt->dmesg_max_cnt;
+ /* no need to try next zone, free last zone buffer */
+ kfree(zone->oldbuf);
+ zone->oldbuf = NULL;
+ return ret;
+ }
+
+ pr_debug("zone %u may be broken, try next dmesg zone\n",
+ zonenum);
+ kfree(zone->buffer);
+ zone->buffer = zone->oldbuf;
+ zone->oldbuf = NULL;
+ }
+
+ return -EBUSY;
}

static int notrace blkz_dmesg_write(struct blkz_context *cxt,
@@ -806,7 +847,6 @@ static int notrace blkz_pstore_write(struct pstore_record *record)
}
}

-#define READ_NEXT_ZONE ((ssize_t)(-1024))
static struct blkz_zone *blkz_read_next_zone(struct blkz_context *cxt)
{
struct blkz_zone *zone = NULL;
@@ -867,7 +907,7 @@ static ssize_t blkz_dmesg_read(struct blkz_zone *zone,
if (blkz_read_dmesg_hdr(zone, record)) {
atomic_set(&zone->buffer->datalen, 0);
atomic_set(&zone->dirty, 0);
- return READ_NEXT_ZONE;
+ return -ENEXT;
}
size -= sizeof(struct blkz_dmesg_header);

@@ -892,7 +932,7 @@ static ssize_t blkz_dmesg_read(struct blkz_zone *zone,
if (unlikely(blkz_zone_read(zone, record->buf + hlen, size,
sizeof(struct blkz_dmesg_header)) < 0)) {
kfree(record->buf);
- return READ_NEXT_ZONE;
+ return -ENEXT;
}

return size + hlen;
@@ -906,7 +946,7 @@ static ssize_t blkz_record_read(struct blkz_zone *zone,

buf = (struct blkz_buffer *)zone->oldbuf;
if (!buf)
- return READ_NEXT_ZONE;
+ return -ENEXT;

size = atomic_read(&buf->datalen);
start = atomic_read(&buf->start);
@@ -956,7 +996,7 @@ static ssize_t blkz_pstore_read(struct pstore_record *record)
}

ret = readop(zone, record);
- if (ret == READ_NEXT_ZONE)
+ if (ret == -ENEXT)
goto next_zone;
return ret;
}
diff --git a/include/linux/blkoops.h b/include/linux/blkoops.h
index 8f40f225545d..71c596fd4cc8 100644
--- a/include/linux/blkoops.h
+++ b/include/linux/blkoops.h
@@ -27,6 +27,7 @@
* On error, negative number should be returned. The following returning
* number means more:
* -EBUSY: pstore/blk should try again later.
+ * -ENEXT: this zone is used or broken, pstore/blk should try next one.
* @panic_write:
* The write operation only used for panic.
*
@@ -45,7 +46,8 @@ struct blkoops_device {

/*
* Panic write for block device who should write alignmemt to SECTOR_SIZE.
- * On success, zero should be returned. Others mean error.
+ * On success, zero should be returned. Others mean error except that -ENEXT
+ * means the zone is used or broken, pstore/blk should try next one.
*/
typedef int (*blkoops_blk_panic_write_op)(const char *buf, sector_t start_sect,
sector_t sects);
diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h
index 77704c1b404a..bbbe4fe37f7c 100644
--- a/include/linux/pstore_blk.h
+++ b/include/linux/pstore_blk.h
@@ -6,6 +6,9 @@
#include <linux/types.h>
#include <linux/blkdev.h>

+/* read/write function return -ENEXT means try next zone */
+#define ENEXT ((ssize_t)(1024))
+
/**
* struct blkz_info - backend blkzone driver structure
*
@@ -42,6 +45,7 @@
* On error, negative number should be returned. The following returning
* number means more:
* -EBUSY: pstore/blk should try again later.
+ * -ENEXT: this zone is used or broken, pstore/blk should try next one.
* @panic_write:
* The write operation only used for panic. It's optional if you do not
* care panic record. If panic occur but blkzone do not recover yet, the
--
1.9.1

2020-01-16 10:24:25

by WeiXiong Liao

[permalink] [raw]
Subject: [PATCH 08/11] blkoops: respect for device to pick recorders

It's one of a series of patches for adaptive to MTD device.

MTD device is not block device. The sector of flash (MTD device) will be
broken if erase over limited cycles. Avoid damaging block so fast, we
can not write to a sector frequently. So, the recorders of pstore/blk
like console and ftrace recorder should not be supported.

Besides, mtd device need aligned write/erase size. To avoid
over-erasing/writing flash, we should keep a aligned cache and read old
data to cache before write/erase, which make codes more complex. So,
pmsg do not be supported now because it writes misaligned.

How about dmesg? Luckly, pstore/blk keeps several aligned chunks for
dmesg and uses one by one for wear balance.

So, MTD device for pstore should pick recorders, that is why the patch
here.

Signed-off-by: WeiXiong Liao <[email protected]>
---
Documentation/admin-guide/pstore-block.rst | 9 +++++++++
fs/pstore/blkoops.c | 29 +++++++++++++++++++++--------
include/linux/blkoops.h | 14 +++++++++++++-
3 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/Documentation/admin-guide/pstore-block.rst b/Documentation/admin-guide/pstore-block.rst
index aea6d2664a22..f4fc205406aa 100644
--- a/Documentation/admin-guide/pstore-block.rst
+++ b/Documentation/admin-guide/pstore-block.rst
@@ -164,6 +164,15 @@ It is only requested by block device which is registered by
``blkoops_register_blkdev``. It's the major device number of registered
devices, by which blkoops can get the matching driver for @blkdev.

+flags
+~~~~~
+
+Refer to macro starting with *BLKOOPS_DEV_SUPPORT_* which is defined in
+*linux/blkoops.h*. They tell us that which pstore/blk recorders this device
+supports. Default zero means all recorders for compatible, witch is the same
+as BLKOOPS_DEV_SUPPORT_ALL. Recorder works only when chunk size is not zero
+and device support.
+
total_size
~~~~~~~~~~

diff --git a/fs/pstore/blkoops.c b/fs/pstore/blkoops.c
index 8437b74d4a0f..85008a839a17 100644
--- a/fs/pstore/blkoops.c
+++ b/fs/pstore/blkoops.c
@@ -143,9 +143,16 @@ int blkoops_register_device(struct blkoops_device *bo_dev)
return -ENOMEM;
}

-#define verify_size(name, defsize, alignsize) { \
- long _##name_ = (name); \
- if (_##name_ < 0) \
+ /* zero means all recorders for compatible */
+ if (bo_dev->flags == BLKOOPS_DEV_SUPPORT_DEFAULT)
+ bo_dev->flags = BLKOOPS_DEV_SUPPORT_ALL;
+#define verify_size(name, defsize, alignsize, enable) { \
+ long _##name_; \
+ if (!(enable)) \
+ _##name_ = 0; \
+ else if ((name) >= 0) \
+ _##name_ = (name); \
+ else \
_##name_ = (defsize); \
_##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
if (_##name_ & (alignsize - 1)) { \
@@ -157,10 +164,14 @@ int blkoops_register_device(struct blkoops_device *bo_dev)
bzinfo->name = _##name_; \
}

- verify_size(dmesg_size, DEFAULT_DMESG_SIZE, 4096);
- verify_size(pmsg_size, DEFAULT_PMSG_SIZE, 4096);
- verify_size(console_size, DEFAULT_CONSOLE_SIZE, 4096);
- verify_size(ftrace_size, DEFAULT_FTRACE_SIZE, 4096);
+ verify_size(dmesg_size, DEFAULT_DMESG_SIZE, 4096,
+ bo_dev->flags & BLKOOPS_DEV_SUPPORT_DMESG);
+ verify_size(pmsg_size, DEFAULT_PMSG_SIZE, 4096,
+ bo_dev->flags & BLKOOPS_DEV_SUPPORT_PMSG);
+ verify_size(console_size, DEFAULT_CONSOLE_SIZE, 4096,
+ bo_dev->flags & BLKOOPS_DEV_SUPPORT_CONSOLE);
+ verify_size(ftrace_size, DEFAULT_FTRACE_SIZE, 4096,
+ bo_dev->flags & BLKOOPS_DEV_SUPPORT_FTRACE);
#undef verify_size
dump_oops = !!(dump_oops < 0 ? DEFAULT_DUMP_OOPS : dump_oops);

@@ -351,6 +362,7 @@ static ssize_t blkoops_blk_panic_write(const char *buf, size_t size,
* register block device to blkoops
* @major: the major device number of registering device
* @panic_write: the write interface for panic case.
+ * @flags: Refer to macro starting with BLKOOPS_DEV_SUPPORT.
*
* It is ONLY used for block device to register to blkoops. In this case,
* the module parameter @blkdev must be valid. Generic read/write interfaces
@@ -364,7 +376,7 @@ static ssize_t blkoops_blk_panic_write(const char *buf, size_t size,
* panic occurs but pstore/blk does not recover yet, the first zone of dmesg
* will be used.
*/
-int blkoops_register_blkdev(unsigned int major,
+int blkoops_register_blkdev(unsigned int major, unsigned int flags,
blkoops_blk_panic_write_op panic_write)
{
struct block_device *bdev;
@@ -387,6 +399,7 @@ int blkoops_register_blkdev(unsigned int major,
if (bo_dev.total_size == 0)
goto err_put_bdev;
bo_dev.panic_write = panic_write ? blkoops_blk_panic_write : NULL;
+ bo_dev.flags = flags;
bo_dev.read = blkoops_generic_blk_read;
bo_dev.write = blkoops_generic_blk_write;

diff --git a/include/linux/blkoops.h b/include/linux/blkoops.h
index 71c596fd4cc8..bc7665d14a98 100644
--- a/include/linux/blkoops.h
+++ b/include/linux/blkoops.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
#include <linux/blkdev.h>
#include <linux/pstore_blk.h>
+#include <linux/bitops.h>

/**
* struct blkoops_device - backend blkoops driver structure.
@@ -14,6 +15,10 @@
* blkoops_register_device(). If block device, you are strongly recommended
* to use blkoops_register_blkdev().
*
+ * @flags:
+ * Refer to macro starting with BLKOOPS_DEV_SUPPORT_. These macros tell
+ * us that which pstore/blk recorders this device supports. Zero means
+ * all recorders for compatible.
* @total_size:
* The total size in bytes pstore/blk can use. It must be greater than
* 4096 and be multiple of 4096.
@@ -38,6 +43,13 @@
* On error, negative number should be returned.
*/
struct blkoops_device {
+ unsigned int flags;
+#define BLKOOPS_DEV_SUPPORT_ALL UINT_MAX
+#define BLKOOPS_DEV_SUPPORT_DEFAULT (0)
+#define BLKOOPS_DEV_SUPPORT_DMESG BIT(0)
+#define BLKOOPS_DEV_SUPPORT_PMSG BIT(1)
+#define BLKOOPS_DEV_SUPPORT_CONSOLE BIT(2)
+#define BLKOOPS_DEV_SUPPORT_FTRACE BIT(3)
unsigned long total_size;
blkz_read_op read;
blkz_write_op write;
@@ -54,7 +66,7 @@ typedef int (*blkoops_blk_panic_write_op)(const char *buf, sector_t start_sect,

int blkoops_register_device(struct blkoops_device *bo_dev);
void blkoops_unregister_device(struct blkoops_device *bo_dev);
-int blkoops_register_blkdev(unsigned int major,
+int blkoops_register_blkdev(unsigned int major, unsigned int flags,
blkoops_blk_panic_write_op panic_write);
void blkoops_unregister_blkdev(unsigned int major);
int blkoops_blkdev_info(dev_t *devt, sector_t *nr_sects, sector_t *start_sect);
--
1.9.1

2020-01-16 10:30:59

by WeiXiong Liao

[permalink] [raw]
Subject: [PATCH 04/11] pstore/blk: blkoops: support console recorder

Support recorder for console. To enable console recorder, just make
console_size be greater than 0 and a multiple of 4096.

Signed-off-by: WeiXiong Liao <[email protected]>
---
fs/pstore/Kconfig | 12 ++++++
fs/pstore/blkoops.c | 11 +++++
fs/pstore/blkzone.c | 100 ++++++++++++++++++++++++++++++++++-----------
include/linux/blkoops.h | 6 ++-
include/linux/pstore_blk.h | 8 +++-
5 files changed, 111 insertions(+), 26 deletions(-)

diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index 656d63dc3f01..af83ae59f31a 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -198,6 +198,18 @@ config PSTORE_BLKOOPS_PMSG_SIZE
NOTE that, both kconfig and module parameters can configure blkoops,
but module parameters have priority over kconfig.

+config PSTORE_BLKOOPS_CONSOLE_SIZE
+ int "console size in kbytes for blkoops"
+ depends on PSTORE_BLKOOPS
+ depends on PSTORE_CONSOLE
+ default 64
+ help
+ This just sets size of console (console_size) for pstore/blk. The
+ value must be a multiple of 4096.
+
+ NOTE that, both kconfig and module parameters can configure blkoops,
+ but module parameters have priority over kconfig.
+
config PSTORE_BLKOOPS_BLKDEV
string "block device for blkoops"
depends on PSTORE_BLKOOPS
diff --git a/fs/pstore/blkoops.c b/fs/pstore/blkoops.c
index 846ba8bc930f..8e1235308cdb 100644
--- a/fs/pstore/blkoops.c
+++ b/fs/pstore/blkoops.c
@@ -35,6 +35,10 @@
module_param(pmsg_size, long, 0400);
MODULE_PARM_DESC(pmsg_size, "pmsg size in kbytes");

+static long console_size = -1;
+module_param(console_size, long, 0400);
+MODULE_PARM_DESC(console_size, "console size in kbytes");
+
static int dump_oops = -1;
module_param(dump_oops, int, 0400);
MODULE_PARM_DESC(total_size, "whether dump oops");
@@ -85,6 +89,12 @@
#define DEFAULT_PMSG_SIZE 0
#endif

+#ifdef CONFIG_PSTORE_BLKOOPS_CONSOLE_SIZE
+#define DEFAULT_CONSOLE_SIZE CONFIG_PSTORE_BLKOOPS_CONSOLE_SIZE
+#else
+#define DEFAULT_CONSOLE_SIZE 0
+#endif
+
#ifdef CONFIG_PSTORE_BLKOOPS_DUMP_OOPS
#define DEFAULT_DUMP_OOPS CONFIG_PSTORE_BLKOOPS_DUMP_OOPS
#else
@@ -139,6 +149,7 @@ int blkoops_register_device(struct blkoops_device *bo_dev)

verify_size(dmesg_size, DEFAULT_DMESG_SIZE, 4096);
verify_size(pmsg_size, DEFAULT_PMSG_SIZE, 4096);
+ verify_size(console_size, DEFAULT_CONSOLE_SIZE, 4096);
#undef verify_size
dump_oops = !!(dump_oops < 0 ? DEFAULT_DUMP_OOPS : dump_oops);

diff --git a/fs/pstore/blkzone.c b/fs/pstore/blkzone.c
index 8716c51d7f98..679aaf598e9e 100644
--- a/fs/pstore/blkzone.c
+++ b/fs/pstore/blkzone.c
@@ -104,9 +104,11 @@ struct blkz_zone {
struct blkz_context {
struct blkz_zone **dbzs; /* dmesg block zones */
struct blkz_zone *pbz; /* Pmsg block zone */
+ struct blkz_zone *cbz; /* console block zone */
unsigned int dmesg_max_cnt;
unsigned int dmesg_read_cnt;
unsigned int pmsg_read_cnt;
+ unsigned int console_read_cnt;
unsigned int dmesg_write_cnt;
/*
* the counter should be recovered when recover.
@@ -127,6 +129,9 @@ struct blkz_context {
};
static struct blkz_context blkz_cxt;

+static void blkz_flush_all_dirty_zones(struct work_struct *);
+static DECLARE_WORK(blkz_cleaner, blkz_flush_all_dirty_zones);
+
enum blkz_flush_mode {
FLUSH_NONE = 0,
FLUSH_PART,
@@ -215,6 +220,9 @@ static int blkz_zone_write(struct blkz_zone *zone,
return 0;
set_dirty:
atomic_set(&zone->dirty, true);
+ /* flush dirty zones nicely */
+ if (wcnt == -EBUSY && !is_on_panic())
+ schedule_work(&blkz_cleaner);
return -EBUSY;
}

@@ -281,6 +289,15 @@ static int blkz_move_zone(struct blkz_zone *old, struct blkz_zone *new)
return 0;
}

+static void blkz_flush_all_dirty_zones(struct work_struct *work)
+{
+ struct blkz_context *cxt = &blkz_cxt;
+
+ blkz_flush_dirty_zone(cxt->pbz);
+ blkz_flush_dirty_zone(cxt->cbz);
+ blkz_flush_dirty_zones(cxt->dbzs, cxt->dmesg_max_cnt);
+}
+
static int blkz_recover_dmesg_data(struct blkz_context *cxt)
{
struct blkz_info *info = cxt->bzinfo;
@@ -434,15 +451,13 @@ static int blkz_recover_dmesg(struct blkz_context *cxt)
return ret;
}

-static int blkz_recover_pmsg(struct blkz_context *cxt)
+static int blkz_recover_zone(struct blkz_context *cxt, struct blkz_zone *zone)
{
struct blkz_info *info = cxt->bzinfo;
struct blkz_buffer *oldbuf;
- struct blkz_zone *zone = NULL;
int ret = 0;
ssize_t rcnt, len;

- zone = cxt->pbz;
if (!zone || zone->oldbuf)
return 0;

@@ -508,7 +523,11 @@ static inline int blkz_recovery(struct blkz_context *cxt)
if (ret)
goto recover_fail;

- ret = blkz_recover_pmsg(cxt);
+ ret = blkz_recover_zone(cxt, cxt->pbz);
+ if (ret)
+ goto recover_fail;
+
+ ret = blkz_recover_zone(cxt, cxt->cbz);
if (ret)
goto recover_fail;

@@ -527,6 +546,7 @@ static int blkz_pstore_open(struct pstore_info *psi)

cxt->dmesg_read_cnt = 0;
cxt->pmsg_read_cnt = 0;
+ cxt->console_read_cnt = 0;
return 0;
}

@@ -554,7 +574,7 @@ static inline int blkz_dmesg_erase(struct blkz_context *cxt,
return blkz_zone_write(zone, FLUSH_META, NULL, 0, 0);
}

-static inline int blkz_pmsg_erase(struct blkz_context *cxt,
+static inline int blkz_record_erase(struct blkz_context *cxt,
struct blkz_zone *zone)
{
if (unlikely(!blkz_old_ok(zone)))
@@ -581,9 +601,10 @@ static int blkz_pstore_erase(struct pstore_record *record)
case PSTORE_TYPE_DMESG:
return blkz_dmesg_erase(cxt, cxt->dbzs[record->id]);
case PSTORE_TYPE_PMSG:
- return blkz_pmsg_erase(cxt, cxt->pbz);
- default:
- return -EINVAL;
+ return blkz_record_erase(cxt, cxt->pbz);
+ case PSTORE_TYPE_CONSOLE:
+ return blkz_record_erase(cxt, cxt->cbz);
+ default: return -EINVAL;
}
}

@@ -668,17 +689,15 @@ static int notrace blkz_dmesg_write(struct blkz_context *cxt,
return 0;
}

-static int notrace blkz_pmsg_write(struct blkz_context *cxt,
- struct pstore_record *record)
+static int notrace blkz_record_write(struct blkz_context *cxt,
+ struct blkz_zone *zone, struct pstore_record *record)
{
- struct blkz_zone *zone;
size_t start, rem;
int cnt = record->size;
bool is_full_data = false;
char *buf = record->buf;

- zone = cxt->pbz;
- if (!zone)
+ if (!zone || !record)
return -ENOSPC;

if (atomic_read(&zone->buffer->datalen) >= zone->buffer_size)
@@ -725,11 +744,20 @@ static int notrace blkz_pstore_write(struct pstore_record *record)
record->reason == KMSG_DUMP_PANIC)
atomic_set(&cxt->on_panic, 1);

+ /*
+ * if on panic, do not write except dmesg records
+ * Fix case that panic_write prints log which wakes up console recorder.
+ */
+ if (is_on_panic() && record->type != PSTORE_TYPE_DMESG)
+ return -EBUSY;
+
switch (record->type) {
case PSTORE_TYPE_DMESG:
return blkz_dmesg_write(cxt, record);
+ case PSTORE_TYPE_CONSOLE:
+ return blkz_record_write(cxt, cxt->cbz, record);
case PSTORE_TYPE_PMSG:
- return blkz_pmsg_write(cxt, record);
+ return blkz_record_write(cxt, cxt->pbz, record);
default:
return -EINVAL;
}
@@ -753,6 +781,13 @@ static struct blkz_zone *blkz_read_next_zone(struct blkz_context *cxt)
return zone;
}

+ if (cxt->console_read_cnt == 0) {
+ cxt->console_read_cnt++;
+ zone = cxt->cbz;
+ if (blkz_old_ok(zone))
+ return zone;
+ }
+
return NULL;
}

@@ -814,7 +849,7 @@ static ssize_t blkz_dmesg_read(struct blkz_zone *zone,
return size + hlen;
}

-static ssize_t blkz_pmsg_read(struct blkz_zone *zone,
+static ssize_t blkz_record_read(struct blkz_zone *zone,
struct pstore_record *record)
{
size_t size, start;
@@ -840,7 +875,7 @@ static ssize_t blkz_pmsg_read(struct blkz_zone *zone,
static ssize_t blkz_pstore_read(struct pstore_record *record)
{
struct blkz_context *cxt = record->psi->data;
- ssize_t (*blkz_read)(struct blkz_zone *zone,
+ ssize_t (*readop)(struct blkz_zone *zone,
struct pstore_record *record);
struct blkz_zone *zone;
ssize_t ret;
@@ -858,17 +893,18 @@ static ssize_t blkz_pstore_read(struct pstore_record *record)
record->type = zone->type;
switch (record->type) {
case PSTORE_TYPE_DMESG:
- blkz_read = blkz_dmesg_read;
+ readop = blkz_dmesg_read;
record->id = cxt->dmesg_read_cnt - 1;
break;
+ case PSTORE_TYPE_CONSOLE:
case PSTORE_TYPE_PMSG:
- blkz_read = blkz_pmsg_read;
+ readop = blkz_record_read;
break;
default:
goto next_zone;
}

- ret = blkz_read(zone, record);
+ ret = readop(zone, record);
if (ret == READ_NEXT_ZONE)
goto next_zone;
return ret;
@@ -1016,15 +1052,25 @@ static int blkz_cut_zones(struct blkz_context *cxt)
goto fail_out;
}

+ off_size += info->console_size;
+ cxt->cbz = blkz_init_zone(PSTORE_TYPE_CONSOLE, &off,
+ info->console_size);
+ if (IS_ERR(cxt->cbz)) {
+ err = PTR_ERR(cxt->cbz);
+ goto free_pmsg;
+ }
+
cxt->dbzs = blkz_init_zones(PSTORE_TYPE_DMESG, &off,
info->total_size - off_size,
info->dmesg_size, &cxt->dmesg_max_cnt);
if (IS_ERR(cxt->dbzs)) {
err = PTR_ERR(cxt->dbzs);
- goto free_pmsg;
+ goto free_console;
}

return 0;
+free_console:
+ blkz_free_zone(&cxt->cbz);
free_pmsg:
blkz_free_zone(&cxt->pbz);
fail_out:
@@ -1042,7 +1088,7 @@ int blkz_register(struct blkz_info *info)
return -EINVAL;
}

- if (!info->dmesg_size && !info->pmsg_size) {
+ if (!info->dmesg_size && !info->pmsg_size && !info->console_size) {
pr_warn("at least one of the records be non-zero\n");
return -EINVAL;
}
@@ -1070,6 +1116,7 @@ int blkz_register(struct blkz_info *info)
check_size(total_size, 4096);
check_size(dmesg_size, SECTOR_SIZE);
check_size(pmsg_size, SECTOR_SIZE);
+ check_size(console_size, SECTOR_SIZE);

#undef check_size

@@ -1102,6 +1149,7 @@ int blkz_register(struct blkz_info *info)
pr_debug("\ttotal size : %ld Bytes\n", info->total_size);
pr_debug("\tdmesg size : %ld Bytes\n", info->dmesg_size);
pr_debug("\tpmsg size : %ld Bytes\n", info->pmsg_size);
+ pr_debug("\tconsole size : %ld Bytes\n", info->console_size);

err = blkz_cut_zones(cxt);
if (err) {
@@ -1123,11 +1171,15 @@ int blkz_register(struct blkz_info *info)
cxt->pstore.flags |= PSTORE_FLAGS_DMESG;
if (info->pmsg_size)
cxt->pstore.flags |= PSTORE_FLAGS_PMSG;
+ if (info->console_size)
+ cxt->pstore.flags |= PSTORE_FLAGS_CONSOLE;

- pr_info("Registered %s as blkzone backend for %s%s%s\n", info->name,
+ pr_info("Registered %s as blkzone backend for %s%s%s%s\n",
+ info->name,
cxt->dbzs && cxt->bzinfo->dump_oops ? "Oops " : "",
cxt->dbzs && cxt->bzinfo->panic_write ? "Panic " : "",
- cxt->pbz ? "Pmsg" : "");
+ cxt->pbz ? "Pmsg " : "",
+ cxt->cbz ? "Console" : "");

err = pstore_register(&cxt->pstore);
if (err) {
@@ -1154,6 +1206,8 @@ void blkz_unregister(struct blkz_info *info)
{
struct blkz_context *cxt = &blkz_cxt;

+ flush_work(&blkz_cleaner);
+
pstore_unregister(&cxt->pstore);
kfree(cxt->pstore.buf);
cxt->pstore.bufsize = 0;
diff --git a/include/linux/blkoops.h b/include/linux/blkoops.h
index fe63739309aa..8f40f225545d 100644
--- a/include/linux/blkoops.h
+++ b/include/linux/blkoops.h
@@ -23,8 +23,10 @@
* Both of the @size and @offset parameters on this interface are
* the relative size of the space provided, not the whole disk/flash.
*
- * On success, the number of bytes read should be returned.
- * On error, negative number should be returned.
+ * On success, the number of bytes read/write should be returned.
+ * On error, negative number should be returned. The following returning
+ * number means more:
+ * -EBUSY: pstore/blk should try again later.
* @panic_write:
* The write operation only used for panic.
*
diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h
index af06be25bd01..546375e04419 100644
--- a/include/linux/pstore_blk.h
+++ b/include/linux/pstore_blk.h
@@ -22,6 +22,9 @@
* @pmsg_size:
* The size of zone for pmsg. Zero means disabled, othewise, it must be
* multiple of SECTOR_SIZE(512).
+ * @console_size:
+ * The size of zone for console. Zero means disabled, othewise, it must
+ * be multiple of SECTOR_SIZE(512).
* @dump_oops:
* Dump oops and panic log or only panic.
* @read, @write:
@@ -33,7 +36,9 @@
* the relative size of the space provided, not the whole disk/flash.
*
* On success, the number of bytes read/write should be returned.
- * On error, negative number should be returned.
+ * On error, negative number should be returned. The following returning
+ * number means more:
+ * -EBUSY: pstore/blk should try again later.
* @panic_write:
* The write operation only used for panic. It's optional if you do not
* care panic record. If panic occur but blkzone do not recover yet, the
@@ -54,6 +59,7 @@ struct blkz_info {
unsigned long total_size;
unsigned long dmesg_size;
unsigned long pmsg_size;
+ unsigned long console_size;
int dump_oops;
blkz_read_op read;
blkz_write_op write;
--
1.9.1