Directly mapped zones have a different semantic from usual pstore zones.
Such zones use a pointer to data in their buffer struct, instead of
keeping the buffer locally.
The data pointer and size is then passed to the backend for further use.
Having a different semantics, backends supporting only these do not offer
read/write ops, and only new register_dmr and unregister_dmr ops.
Ofcourse, a backend could support both classic zones and directly mapped.
Directly mapped zones have the advantage of not being passed through
in the event of a crashed, but rather at registration time.

Signed-off-by: Eugen Hristev <[email protected]>
---
 fs/pstore/platform.c        |  1 +
 fs/pstore/smem.c            |  6 ++---
 fs/pstore/zone.c            | 45 ++++++++++++++++++++++++++++---------
 include/linux/pstore.h      |  4 ++++
 include/linux/pstore_zone.h |  3 +++
 5 files changed, 45 insertions(+), 14 deletions(-)

diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index f56b066ab80c..e20e60b88727 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -51,6 +51,7 @@ static const char * const pstore_type_names[] = {
        "powerpc-common",
        "pmsg",
        "powerpc-opal",
+       "directly-mapped",
 };
 
 static int pstore_new_entry;
diff --git a/fs/pstore/smem.c b/fs/pstore/smem.c
index 9eedd7df5446..684ecc20cae5 100644
--- a/fs/pstore/smem.c
+++ b/fs/pstore/smem.c
@@ -36,15 +36,15 @@ static int __register_pstore_device(struct 
pstore_device_info *dev)
                pr_err("NULL device info\n");
                return -EINVAL;
        }
-       if (!dev->zone.total_size) {
+       if (!dev->zone.total_size && !dev->zone.dmapped_cnt) {
                pr_err("zero sized device\n");
                return -EINVAL;
        }
-       if (!dev->zone.read) {
+       if (!dev->zone.read && !dev->zone.dmapped_cnt) {
                pr_err("no read handler for device\n");
                return -EINVAL;
        }
-       if (!dev->zone.write) {
+       if (!dev->zone.write && !dev->zone.dmapped_cnt) {
                pr_err("no write handler for device\n");
                return -EINVAL;
        }
diff --git a/fs/pstore/zone.c b/fs/pstore/zone.c
index 5fa2fa2e7aa7..affa4370208c 100644
--- a/fs/pstore/zone.c
+++ b/fs/pstore/zone.c
@@ -113,6 +113,7 @@ struct psz_context {
        struct pstore_zone *ppsz;
        struct pstore_zone *cpsz;
        struct pstore_zone **fpszs;
+       struct pstore_zone **dmszs;
        unsigned int kmsg_max_cnt;
        unsigned int kmsg_read_cnt;
        unsigned int kmsg_write_cnt;
@@ -120,6 +121,7 @@ struct psz_context {
        unsigned int console_read_cnt;
        unsigned int ftrace_max_cnt;
        unsigned int ftrace_read_cnt;
+       unsigned int dmapped_max_cnt;
        /*
         * These counters should be calculated during recovery.
         * It records the oops/panic times after crashes rather than boots.
@@ -1148,6 +1150,8 @@ static void psz_free_all_zones(struct psz_context *cxt)
                psz_free_zone(&cxt->cpsz);
        if (cxt->fpszs)
                psz_free_zones(&cxt->fpszs, &cxt->ftrace_max_cnt);
+       if (cxt->dmszs)
+               psz_free_zones(&cxt->dmszs, &cxt->dmapped_max_cnt);
 }
 
 static struct pstore_zone *psz_init_zone(enum pstore_type_id type,
@@ -1160,9 +1164,9 @@ static struct pstore_zone *psz_init_zone(enum 
pstore_type_id type,
        if (!size)
                return NULL;
 
-       if (*off + size > info->total_size) {
-               pr_err("no room for %s (0x%zx@0x%llx over 0x%lx)\n",
-                       name, size, *off, info->total_size);
+       if (*off + size > info->total_size && type != PSTORE_TYPE_DMAPPED) {
+               pr_err("no room for %s type %d (0x%zx@0x%llx over 0x%lx)\n",
+                       name, type, size, *off, info->total_size);
                return ERR_PTR(-ENOMEM);
        }
 
@@ -1170,7 +1174,8 @@ static struct pstore_zone *psz_init_zone(enum 
pstore_type_id type,
        if (!zone)
                return ERR_PTR(-ENOMEM);
 
-       zone->buffer = kmalloc(size, GFP_KERNEL);
+       zone->buffer = kmalloc(type == PSTORE_TYPE_DMAPPED ?
+                              sizeof(struct psz_buffer) : size, GFP_KERNEL);
        if (!zone->buffer) {
                kfree(zone);
                return ERR_PTR(-ENOMEM);
@@ -1179,7 +1184,10 @@ static struct pstore_zone *psz_init_zone(enum 
pstore_type_id type,
        zone->off = *off;
        zone->name = name;
        zone->type = type;
-       zone->buffer_size = size - sizeof(struct psz_buffer);
+       if (zone->type == PSTORE_TYPE_DMAPPED)
+               zone->buffer_size = 0;
+       else
+               zone->buffer_size = size - sizeof(struct psz_buffer);
        zone->buffer->sig = type ^ PSZ_SIG;
        zone->oldbuf = NULL;
        atomic_set(&zone->dirty, 0);
@@ -1188,8 +1196,9 @@ static struct pstore_zone *psz_init_zone(enum 
pstore_type_id type,
 
        *off += size;
 
-       pr_debug("pszone %s: off 0x%llx, %zu header, %zu data\n", zone->name,
-                       zone->off, sizeof(*zone->buffer), zone->buffer_size);
+       pr_debug("pszone %s: off 0x%llx, %zu header, %zu data %s\n", zone->name,
+                       zone->off, sizeof(*zone->buffer), zone->buffer_size,
+                       zone->type == PSTORE_TYPE_DMAPPED ? " dmapped " : "");
        return zone;
 }
 
@@ -1206,7 +1215,7 @@ static struct pstore_zone **psz_init_zones(enum 
pstore_type_id type,
        if (!total_size || !record_size)
                return NULL;
 
-       if (*off + total_size > info->total_size) {
+       if (*off + total_size > info->total_size && type != 
PSTORE_TYPE_DMAPPED) {
                pr_err("no room for zones %s (0x%zx@0x%llx over 0x%lx)\n",
                        name, total_size, *off, info->total_size);
                return ERR_PTR(-ENOMEM);
@@ -1245,6 +1254,15 @@ static int psz_alloc_zones(struct psz_context *cxt)
        int err;
        size_t off_size = 0;
 
+       cxt->dmszs = psz_init_zones(PSTORE_TYPE_DMAPPED, &off,
+                       info->dmapped_cnt,
+                       1, &cxt->dmapped_max_cnt);
+       if (IS_ERR(cxt->dmszs)) {
+               err = PTR_ERR(cxt->dmszs);
+               cxt->dmszs = NULL;
+               goto free_out;
+       }
+
        off_size += info->pmsg_size;
        cxt->ppsz = psz_init_zone(PSTORE_TYPE_PMSG, &off, info->pmsg_size);
        if (IS_ERR(cxt->ppsz)) {
@@ -1302,7 +1320,7 @@ int register_pstore_zone(struct pstore_zone_info *info)
        int err = -EINVAL;
        struct psz_context *cxt = &pstore_zone_cxt;
 
-       if (info->total_size < 4096) {
+       if (info->total_size < 4096 && !info->dmapped_cnt) {
                pr_warn("total_size must be >= 4096\n");
                return -EINVAL;
        }
@@ -1312,7 +1330,7 @@ int register_pstore_zone(struct pstore_zone_info *info)
        }
 
        if (!info->kmsg_size && !info->pmsg_size && !info->console_size &&
-           !info->ftrace_size) {
+           !info->ftrace_size && !info->dmapped_cnt) {
                pr_warn("at least one record size must be non-zero\n");
                return -EINVAL;
        }
@@ -1345,7 +1363,7 @@ int register_pstore_zone(struct pstore_zone_info *info)
         * if no @read, pstore may mount failed.
         * if no @write, pstore do not support to remove record file.
         */
-       if (!info->read || !info->write) {
+       if (!info->dmapped_cnt && (!info->read || !info->write)) {
                pr_err("no valid general read/write interface\n");
                return -EINVAL;
        }
@@ -1365,6 +1383,7 @@ int register_pstore_zone(struct pstore_zone_info *info)
        pr_debug("\tpmsg size : %ld Bytes\n", info->pmsg_size);
        pr_debug("\tconsole size : %ld Bytes\n", info->console_size);
        pr_debug("\tftrace size : %ld Bytes\n", info->ftrace_size);
+       pr_debug("\tdmapped areas : %ld\n", info->dmapped_cnt);
 
        err = psz_alloc_zones(cxt);
        if (err) {
@@ -1406,6 +1425,10 @@ int register_pstore_zone(struct pstore_zone_info *info)
                cxt->pstore.flags |= PSTORE_FLAGS_FTRACE;
                pr_cont(" ftrace");
        }
+       if (info->dmapped_cnt) {
+               cxt->pstore.flags |= PSTORE_FLAGS_DMAPPED;
+               pr_cont(" dmapped");
+       }
        pr_cont("\n");
 
        err = pstore_register(&cxt->pstore);
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index fed601053c51..8360d94c96b6 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -39,6 +39,7 @@ enum pstore_type_id {
        PSTORE_TYPE_PMSG        = 7,
        PSTORE_TYPE_PPC_OPAL    = 8,
 
+       PSTORE_TYPE_DMAPPED     = 9,
        /* End of the list */
        PSTORE_TYPE_MAX
 };
@@ -199,6 +200,8 @@ struct pstore_info {
        int             (*write_user)(struct pstore_record *record,
                                      const char __user *buf);
        int             (*erase)(struct pstore_record *record);
+       int             (*register_dmr)(struct pstore_record *record);
+       int             (*unregister_dmr)(struct pstore_record *record);
 };
 
 /* Supported frontends */
@@ -206,6 +209,7 @@ struct pstore_info {
 #define PSTORE_FLAGS_CONSOLE   BIT(1)
 #define PSTORE_FLAGS_FTRACE    BIT(2)
 #define PSTORE_FLAGS_PMSG      BIT(3)
+#define PSTORE_FLAGS_DMAPPED   BIT(4)
 
 extern int pstore_register(struct pstore_info *);
 extern void pstore_unregister(struct pstore_info *);
diff --git a/include/linux/pstore_zone.h b/include/linux/pstore_zone.h
index 284364234162..a74d0cc75577 100644
--- a/include/linux/pstore_zone.h
+++ b/include/linux/pstore_zone.h
@@ -48,10 +48,13 @@ struct pstore_zone_info {
        unsigned long pmsg_size;
        unsigned long console_size;
        unsigned long ftrace_size;
+       unsigned long dmapped_cnt;
        pstore_zone_read_op read;
        pstore_zone_write_op write;
        pstore_zone_erase_op erase;
        pstore_zone_write_op panic_write;
+       int (*register_dmr)(char *, int, void *, size_t);
+       int (*unregister_dmr)(void *, size_t);
 };
 
 /**
-- 
2.43.0


Reply via email to