@@ -51,6 +51,7 @@ static const char * const pstore_type_names[] = {
"powerpc-common",
"pmsg",
"powerpc-opal",
+ "directly-mapped",
};
static int pstore_new_entry;
@@ -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;
}
@@ -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);
@@ -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 *);
@@ -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);
};
/**
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 <eugen.hristev@linaro.org> --- 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(-)