[RFC][PATCH -mmotm 3/4] pstore: mtdoops support

From: Seiji Aguchi
Date: Tue Jul 19 2011 - 14:27:45 EST


Hi,

Pstore can support mtdoops with this patch.

fs/pstore/platform.c
- Add "reason" argument to pstore_dump() so that mtdoops can select its behaivor in accordance with each reason

drivers/mtd/Kconfig
- Add "depends on PSTORE" to CONFIG_MTD_OOPS

drivers/mtd/mtdoops.c
- Add pstore_info structure so that mtdoops can call pstore_register()
- Remove kmsg_dump_unregister because pstore doesn't support unregister operation
- Change printk() to DEBUG() in kexec path for avoiding dead lock due to logbuf_lock

drivers/acpi/apei/erst.c
- Add "reason" argument to erst_write()

TODO:
- I don't have any access to machine capable of Memory Technology Device.
Please help to test my patch.

- I haven't implemented reader/eraser callbacks supported by pstore.
- If mtdoops users would like to unload module,
pstore needs to support unregister operation.

Signed-off-by: Seiji Aguchi <seiji.aguchi@xxxxxxx>

---
drivers/acpi/apei/erst.c | 6 ++-
drivers/mtd/Kconfig | 1 +
drivers/mtd/mtdoops.c | 95 +++++++++++++++++++++++++++++++--------------
fs/pstore/platform.c | 5 +-
include/linux/pstore.h | 3 +-
5 files changed, 75 insertions(+), 35 deletions(-)

diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index e6cef8e..ee936b6 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -933,7 +933,8 @@ static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time);
-static u64 erst_writer(enum pstore_type_id type, size_t size);
+static u64 erst_writer(enum pstore_type_id type, size_t size,
+ enum kmsg_dump_reason reason);

static struct pstore_info erst_info = {
.owner = THIS_MODULE,
@@ -1037,7 +1038,8 @@ out:
return (rc < 0) ? rc : (len - sizeof(*rcd));
}

-static u64 erst_writer(enum pstore_type_id type, size_t size)
+static u64 erst_writer(enum pstore_type_id type, size_t size,
+ enum kmsg_dump_reason reason)
{
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index cc02e21..63a6e04 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -301,6 +301,7 @@ config SM_FTL

config MTD_OOPS
tristate "Log panic/oops to an MTD buffer"
+ depends on PSTORE
help
This enables panic and oops messages to be logged to a circular
buffer in a flash partition where it can be read back at some
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 56eac4e..61b1120 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -32,6 +32,7 @@
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/kmsg_dump.h>
+#include <linux/pstore.h>

/* Maximum MTD partition size */
#define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
@@ -54,6 +55,25 @@ 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 unsigned long total_size;
+static int mtdoops_open(struct pstore_info *psi);
+static int mtdoops_close(struct pstore_info *psi);
+static ssize_t mtdoops_reader(u64 *id, enum pstore_type_id *type,
+ struct timespec *time);
+static u64 mtdoops_dumper(enum pstore_type_id type, size_t size,
+ enum kmsg_dump_reason reason);
+static int mtdoops_eraser(u64 record_id);
+
+static struct pstore_info mtdoops_info = {
+ .owner = THIS_MODULE,
+ .name = "mtdoops",
+ .open = mtdoops_open,
+ .close = mtdoops_close,
+ .read = mtdoops_reader,
+ .write = mtdoops_dumper,
+ .erase = mtdoops_eraser
+};
+
static struct mtdoops_context {
struct kmsg_dumper dump;

@@ -229,8 +249,9 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
record_size, &retlen, cxt->oops_buf);

if (retlen != record_size || ret < 0)
- printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n",
- cxt->nextpage * record_size, retlen, record_size, ret);
+ DEBUG(MTD_DEBUG_LEVEL3, "mtdoops: write failure at %ld (%td of "
+ "%ld written), error %d\n", cxt->nextpage * record_size,
+ retlen, record_size, ret);
mark_page_used(cxt, cxt->nextpage);
memset(cxt->oops_buf, 0xff, record_size);

@@ -297,47 +318,62 @@ static void find_next_position(struct mtdoops_context *cxt)
mtdoops_inc_counter(cxt);
}

-static void mtdoops_do_dump(struct kmsg_dumper *dumper,
- enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
- const char *s2, unsigned long l2)
+static int mtdoops_open(struct pstore_info *psi)
+{
+ return 0;
+}
+
+static int mtdoops_close(struct pstore_info *psi)
+{
+ return 0;
+}
+
+static ssize_t mtdoops_reader(u64 *id, enum pstore_type_id *type,
+ struct timespec *time)
{
- struct mtdoops_context *cxt = container_of(dumper,
- struct mtdoops_context, dump);
- unsigned long s1_start, s2_start;
- unsigned long l1_cpy, l2_cpy;
- char *dst;
+ return -EINVAL;
+}
+
+static u64 mtdoops_dumper(enum pstore_type_id type, size_t size,
+ enum kmsg_dump_reason reason)
+{
+ struct mtdoops_context *cxt = &oops_cxt;

if (reason != KMSG_DUMP_OOPS &&
- reason != KMSG_DUMP_PANIC)
- return;
+ reason != KMSG_DUMP_PANIC &&
+ reason != KMSG_DUMP_KEXEC)
+ return 0;

/* Only dump oopses if dump_oops is set */
if (reason == KMSG_DUMP_OOPS && !dump_oops)
- return;
-
- dst = cxt->oops_buf + MTDOOPS_HEADER_SIZE; /* Skip the header */
- l2_cpy = min(l2, record_size - MTDOOPS_HEADER_SIZE);
- l1_cpy = min(l1, record_size - MTDOOPS_HEADER_SIZE - l2_cpy);
-
- s2_start = l2 - l2_cpy;
- s1_start = l1 - l1_cpy;
+ return 0;

- memcpy(dst, s1 + s1_start, l1_cpy);
- memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
+ if (total_size >= record_size)
+ return 0;

/* Panics must be written immediately */
if (reason != KMSG_DUMP_OOPS) {
if (!cxt->mtd->panic_write)
- printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n");
- else
+ DEBUG(MTD_DEBUG_LEVEL3, "mtdoops: Cannot write from "
+ "panic without panic_write\n");
+ else {
mtdoops_write(cxt, 1);
- return;
+ total_size = total_size + size + MTDOOPS_HEADER_SIZE;
+ }
+ return 0;
}

/* For other cases, schedule work to write it "nicely" */
schedule_work(&cxt->work_write);
+ return 0;
}

+static int mtdoops_eraser(u64 record_id)
+{
+ return 0;
+}
+
+
static void mtdoops_notify_add(struct mtd_info *mtd)
{
struct mtdoops_context *cxt = &oops_cxt;
@@ -374,8 +410,10 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
return;
}

- cxt->dump.dump = mtdoops_do_dump;
- err = kmsg_dump_register(&cxt->dump);
+ mutex_init(&mtdoops_info.buf_mutex);
+ mtdoops_info.buf = cxt->oops_buf + MTDOOPS_HEADER_SIZE;
+ mtdoops_info.bufsize = record_size - MTDOOPS_HEADER_SIZE;
+ err = pstore_register(&mtdoops_info);
if (err) {
printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err);
vfree(cxt->oops_page_used);
@@ -396,9 +434,6 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
return;

- if (kmsg_dump_unregister(&cxt->dump) < 0)
- printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n");
-
cxt->mtd = NULL;
flush_work_sync(&cxt->work_erase);
flush_work_sync(&cxt->work_write);
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 061911c..cd42b4e 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -105,7 +105,8 @@ static void pstore_dump(struct kmsg_dumper *dumper,
memcpy(dst, s1 + s1_start, l1_cpy);
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);

- id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy);
+ id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy,
+ reason);
if (reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id,
psinfo->buf, hsize + l1_cpy + l2_cpy,
@@ -220,7 +221,7 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size)

mutex_lock(&psinfo->buf_mutex);
memcpy(psinfo->buf, buf, size);
- id = psinfo->write(type, size);
+ id = psinfo->write(type, size, 0);
if (pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf,
size, CURRENT_TIME, psinfo->erase);
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index 5cf008d..47b974f 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -41,7 +41,8 @@ struct pstore_info {
int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type,
struct timespec *time);
- u64 (*write)(enum pstore_type_id type, size_t size);
+ u64 (*write)(enum pstore_type_id type, size_t size,
+ enum kmsg_dump_reason reason);
int (*erase)(u64 id);
};

--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/