"ramoops.ko".
For more information, see Documentation/admin-guide/ramoops.rst.
+
+config PSTORE_RAM_ANNOTATION_APPEND
+ bool "Allow appending messages to pstore ram annotation"
+ default n
+ depends on PSTORE_RAM
+ help
+ Existing pstore ram annotation stores messages and exports them
+ after reset. Sometimes only after reset can we do some postmortem
+ analysis. This adds function which can be used to append the
+ analysis info to pstore ram annotation. The function should only
+ be used before pstore ram annotation is exported. It's safe to use
+ the function before pstore is initialized. Content will be buffered
+ and merged to annotation-ramoops file.
+ If unsure, say N.
ps->record->buf, ps->total_size);
}
+#define PSTORE_ANNOTATE_MAX_SIZE 0x100
+static ssize_t pstore_file_write(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *sf = file->private_data;
+ struct pstore_private *ps = sf->private;
+ char *buffer;
+ ssize_t ret = 0;
+ ssize_t saved_count = count;
+
+ if (!count || !userbuf)
+ return 0;
+
+ if (ps->record->type != PSTORE_TYPE_ANNOTATE)
+ return count;
+
+ if (count > PSTORE_ANNOTATE_MAX_SIZE)
+ count = PSTORE_ANNOTATE_MAX_SIZE;
+
+ buffer = kmalloc(count + 1, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ if (copy_from_user(buffer, userbuf, count)) {
+ ret = -EFAULT;
+ goto write_out;
+ }
+
+ buffer[count] = '\0';
+ pstore_annotate(buffer);
+ ret = saved_count;
+write_out:
+ kfree(buffer);
+ return ret;
+}
+
static int pstore_file_open(struct inode *inode, struct file *file)
{
struct pstore_private *ps = inode->i_private;
static const struct file_operations pstore_file_operations = {
.open = pstore_file_open,
.read = pstore_file_read,
+ .write = pstore_file_write,
.llseek = pstore_file_llseek,
.release = seq_release,
};
scnprintf(name, sizeof(name), "powerpc-opal-%s-%llu",
record->psi->name, record->id);
break;
+ case PSTORE_TYPE_ANNOTATE:
+ scnprintf(name, sizeof(name), "annotate-%s-%lld", record->psi->name, record->id);
+ break;
case PSTORE_TYPE_UNKNOWN:
scnprintf(name, sizeof(name), "unknown-%s-%llu",
record->psi->name, record->id);
return -EIO;
}
+#if 0
static void allocate_buf_for_compression(void)
{
if (zbackend) {
pr_err("allocate compression buffer error!\n");
}
}
+#endif
static void free_buf_for_compression(void)
{
kmsg_dump_unregister(&pstore_dumper);
}
+
+/* Export function to save device specific power up
+ * data
+ *
+ */
+
+int pstore_annotate(const char *buf)
+{
+ unsigned cnt = strlen(buf);
+ const char *end = buf + cnt;
+
+ if (!psinfo) {
+ pr_warn("device not present!\n");
+ return -ENODEV;
+ }
+ while (buf < end) {
+ unsigned long flags;
+ int ret;
+ struct pstore_record record;
+
+ pstore_record_init(&record, psinfo);
+ record.type = PSTORE_TYPE_ANNOTATE;
+
+ if (cnt > psinfo->bufsize)
+ cnt = psinfo->bufsize;
+
+ if (oops_in_progress) {
+ if (!spin_trylock_irqsave(&psinfo->buf_lock, flags))
+ break;
+ } else {
+ spin_lock_irqsave(&psinfo->buf_lock, flags);
+ }
+ record.buf = (char *)buf;
+ record.size = cnt;
+ ret = psinfo->write(&record);
+ spin_unlock_irqrestore(&psinfo->buf_lock, flags);
+
+ pr_debug("ret %d wrote bytes %d\n", ret, cnt);
+ buf += cnt;
+ cnt = end - buf;
+ }
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(pstore_annotate);
+
+
#ifdef CONFIG_PSTORE_CONSOLE
static void pstore_console_write(struct console *con, const char *s, unsigned c)
{
return -EINVAL;
}
+ // FIX ME!
+ // Disable pstore compression/decompression for ramdom decompression fail
+ // will enable it when DDR is stable
+#if 0
allocate_buf_for_compression();
+#endif
if (pstore_is_mounted())
pstore_get_records(0);
module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400);
MODULE_PARM_DESC(pmsg_size, "size of user space message log");
+static ulong ramoops_annotate_size = MIN_MEM_SIZE;
+module_param_named(annotate_size, ramoops_annotate_size, ulong, 0400);
+MODULE_PARM_DESC(annotate_size, "size of annotation");
+
static unsigned long long mem_address;
module_param_hw(mem_address, ullong, other, 0400);
MODULE_PARM_DESC(mem_address,
struct persistent_ram_zone *cprz; /* Console zone */
struct persistent_ram_zone **fprzs; /* Ftrace zones */
struct persistent_ram_zone *mprz; /* PMSG zone */
+ struct persistent_ram_zone *aprz;
phys_addr_t phys_addr;
unsigned long size;
unsigned int memtype;
size_t console_size;
size_t ftrace_size;
size_t pmsg_size;
+ size_t annotate_size;
int dump_oops;
u32 flags;
struct persistent_ram_ecc_info ecc_info;
unsigned int max_ftrace_cnt;
unsigned int ftrace_read_cnt;
unsigned int pmsg_read_cnt;
+ unsigned int annotate_read_cnt;
struct pstore_info pstore;
};
cxt->console_read_cnt = 0;
cxt->ftrace_read_cnt = 0;
cxt->pmsg_read_cnt = 0;
+ cxt->annotate_read_cnt = 0;
return 0;
}
prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
1, &record->id, &record->type,
PSTORE_TYPE_PMSG, 0);
+ if (!prz_ok(prz)) {
+ prz = ramoops_get_next_prz(&cxt->aprz, &cxt->annotate_read_cnt,
+ 1, &record->id, &record->type, PSTORE_TYPE_ANNOTATE, 0);
+ persistent_ram_annotation_merge(prz);
+ }
/* ftrace is last since it may want to dynamically allocate memory. */
if (!prz_ok(prz)) {
} else if (record->type == PSTORE_TYPE_PMSG) {
pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__);
return -EINVAL;
+ } else if (record->type == PSTORE_TYPE_ANNOTATE) {
+ if (!cxt->aprz)
+ return -ENOMEM;
+ persistent_ram_write(cxt->aprz, record->buf, record->size);
+ return 0;
}
if (record->type != PSTORE_TYPE_DMESG)
* Out of the various dmesg dump types, ramoops is currently designed
* to only store crash logs, rather than storing general kernel logs.
*/
- if (record->reason != KMSG_DUMP_OOPS &&
- record->reason != KMSG_DUMP_PANIC)
+ if (record->reason != KMSG_DUMP_PANIC)
return -EINVAL;
/* Skip Oopes when configured to do so. */
case PSTORE_TYPE_PMSG:
prz = cxt->mprz;
break;
+ case PSTORE_TYPE_ANNOTATE:
+ prz = cxt->aprz;
+ break;
default:
return -EINVAL;
}
parse_size("pmsg-size", pdata->pmsg_size);
parse_size("ecc-size", pdata->ecc_info.ecc_size);
parse_size("flags", pdata->flags);
+ parse_size("ramoops-annotate-size", pdata->annotate_size);
#undef parse_size
if (err < 0)
goto fail_out;
}
-
+
/*
* Only a single ramoops area allowed at a time, so fail extra
* probes.
}
if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size &&
- !pdata->ftrace_size && !pdata->pmsg_size)) {
+ !pdata->ftrace_size && !pdata->pmsg_size &&
+ !pdata->annotate_size)) {
pr_err("The memory size and the record/console size must be "
"non-zero\n");
goto fail_out;
pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size))
pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size);
+ if (pdata->annotate_size && !is_power_of_2(pdata->annotate_size))
+ pdata->annotate_size =
+ rounddown_pow_of_two(pdata->annotate_size);
+
+ pr_debug("All %#lx record %#lx console %#lx ftrace %#lx annotate %#lx\n",
+ pdata->mem_size, pdata->record_size, pdata->console_size,
+ pdata->ftrace_size, pdata->annotate_size);
cxt->size = pdata->mem_size;
cxt->phys_addr = pdata->mem_address;
cxt->console_size = pdata->console_size;
cxt->ftrace_size = pdata->ftrace_size;
cxt->pmsg_size = pdata->pmsg_size;
+ cxt->annotate_size = pdata->annotate_size;
cxt->dump_oops = pdata->dump_oops;
cxt->flags = pdata->flags;
cxt->ecc_info = pdata->ecc_info;
paddr = cxt->phys_addr;
dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size
- - cxt->pmsg_size;
+ - cxt->pmsg_size - cxt->annotate_size;
err = ramoops_init_przs("dump", dev, cxt, &cxt->dprzs, &paddr,
dump_mem_sz, cxt->record_size,
&cxt->max_dump_cnt, 0, 0);
if (err)
goto fail_init_mprz;
+ err = ramoops_init_prz("annotate", dev, cxt, &cxt->aprz, &paddr,
+ cxt->annotate_size, 0);
+
+ if (err) {
+ goto fail_init_aprz;
+ }
+
cxt->pstore.data = cxt;
/*
* Since bufsize is only used for dmesg crash dumps, it
cxt->pstore.flags |= PSTORE_FLAGS_FTRACE;
if (cxt->pmsg_size)
cxt->pstore.flags |= PSTORE_FLAGS_PMSG;
+ if (cxt->annotate_size)
+ cxt->pstore.flags |= PSTORE_FLAGS_ANNOTATE;
err = pstore_register(&cxt->pstore);
if (err) {
kfree(cxt->pstore.buf);
fail_clear:
cxt->pstore.bufsize = 0;
+ cxt->max_dump_cnt = 0;
persistent_ram_free(cxt->mprz);
+fail_init_aprz:
+ persistent_ram_free(cxt->aprz);
fail_init_mprz:
fail_init_fprz:
persistent_ram_free(cxt->cprz);
return ret;
}
+#ifdef CONFIG_PSTORE_RAM_ANNOTATION_APPEND
+struct praa_buf {
+ struct list_head list;
+ int size, space;
+ char data[];
+};
+
+struct persistent_ram_annotation_append_buffer {
+ spinlock_t lock; /* protect list and buf */
+ struct list_head list;
+ struct praa_buf *buf;
+ int total_size;
+ int stop;
+} praa_buffer = {
+ .lock = __SPIN_LOCK_UNLOCKED(praa_buffer.lock),
+ .list = LIST_HEAD_INIT(praa_buffer.list),
+ .stop = 0,
+ .buf = NULL,
+ .total_size = 0,
+};
+
+int persistent_ram_annotation_append(const char *fmt, ...)
+{
+ va_list args;
+ unsigned long flags;
+ int len = 0;
+ char line_buf[512];
+ struct praa_buf *buf;
+
+ va_start(args, fmt);
+ len += vsnprintf(line_buf + len, sizeof(line_buf) - len, fmt, args);
+ va_end(args);
+
+ spin_lock_irqsave(&praa_buffer.lock, flags);
+ if (praa_buffer.stop) {
+ spin_unlock_irqrestore(&praa_buffer.lock, flags);
+ pr_err("%s() called too late by %pf()\n", __func__,
+ __builtin_return_address(0));
+ return 0;
+ }
+
+ while (1) {
+ if (!praa_buffer.buf) {
+ buf = (struct praa_buf *) __get_free_page(GFP_ATOMIC);
+ if (buf) {
+ buf->size = 0;
+ buf->space = PAGE_SIZE - 1 -
+ offsetof(struct praa_buf, data);
+ praa_buffer.buf = buf;
+ } else {
+ pr_err("%s NOMEM\n", __func__);
+ len = 0;
+ break;
+ }
+ }
+ buf = praa_buffer.buf;
+ if (len + 1 > buf->space) {
+ buf->data[buf->size] = '\0';
+ list_add_tail(&buf->list, &praa_buffer.list);
+ praa_buffer.total_size += buf->size;
+ praa_buffer.buf = NULL;
+ continue;
+ }
+ memcpy(&buf->data[buf->size], line_buf, len);
+ buf->space -= len;
+ buf->size += len;
+ break;
+ }
+ spin_unlock_irqrestore(&praa_buffer.lock, flags);
+ return len;
+}
+
+static int persistent_ram_annotation_append_stop(void)
+{
+ int ret;
+ unsigned long flags;
+ struct praa_buf *buf;
+ spin_lock_irqsave(&praa_buffer.lock, flags);
+ if (praa_buffer.stop) {
+ spin_unlock_irqrestore(&praa_buffer.lock, flags);
+ return 0;
+ }
+ praa_buffer.stop = 1;
+ if (praa_buffer.buf) {
+ buf = praa_buffer.buf;
+ praa_buffer.buf = NULL;
+ buf->data[buf->size] = '\0';
+ list_add_tail(&buf->list, &praa_buffer.list);
+ praa_buffer.total_size += buf->size;
+ }
+ ret = praa_buffer.total_size;
+ spin_unlock_irqrestore(&praa_buffer.lock, flags);
+ return ret;
+}
+
+static void persistent_ram_annotation_append_push(char *ptr)
+{
+ unsigned long flags;
+ struct praa_buf *buf, *n;
+
+ spin_lock_irqsave(&praa_buffer.lock, flags);
+ list_for_each_entry_safe(buf, n, &praa_buffer.list, list) {
+ if (ptr) {
+ memcpy(ptr, buf->data, buf->size);
+ ptr += buf->size;
+ }
+ list_del(&buf->list);
+ praa_buffer.total_size -= buf->size;
+ free_page((unsigned long)buf);
+ }
+ spin_unlock_irqrestore(&praa_buffer.lock, flags);
+}
+
+void persistent_ram_annotation_merge(struct persistent_ram_zone *prz)
+{
+ size_t ext_size;
+ char *old_log2;
+
+ ext_size = persistent_ram_annotation_append_stop();
+ if (ext_size) {
+ if (!prz) {
+ persistent_ram_annotation_append_push(NULL);
+ pr_info("%s: discarded %zu\n", __func__, ext_size);
+ return;
+ }
+ old_log2 = krealloc(prz->old_log,
+ prz->old_log_size + ext_size, GFP_KERNEL);
+ if (old_log2) {
+ persistent_ram_annotation_append_push(old_log2 +
+ prz->old_log_size);
+ prz->old_log = old_log2;
+ prz->old_log_size += ext_size;
+ pr_info("%s: merged %zu\n", __func__, ext_size);
+ } else {
+ pr_err("%s: cannot merge %zu\n", __func__, ext_size);
+ persistent_ram_annotation_append_push(NULL);
+ }
+ }
+}
+#endif
+
void persistent_ram_save_old(struct persistent_ram_zone *prz)
{
struct persistent_ram_buffer *buffer = prz->buffer;
prz->buffer = prz->vaddr;
prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
+ pr_info("persistent_ram: paddr: %p, vaddr: %p, buf size = 0x%zx\n",
+ (void *)prz->paddr, (void *)prz->vaddr, prz->buffer_size);
return 0;
}
PSTORE_TYPE_PPC_COMMON = 6,
PSTORE_TYPE_PMSG = 7,
PSTORE_TYPE_PPC_OPAL = 8,
+ PSTORE_TYPE_ANNOTATE = 9,
PSTORE_TYPE_UNKNOWN = 255
};
#define PSTORE_FLAGS_CONSOLE (1 << 1)
#define PSTORE_FLAGS_FTRACE (1 << 2)
#define PSTORE_FLAGS_PMSG (1 << 3)
+#define PSTORE_FLAGS_ANNOTATE (1 << 4)
extern int pstore_register(struct pstore_info *);
extern void pstore_unregister(struct pstore_info *);
extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason);
+extern int pstore_annotate(const char *buf);
struct pstore_ftrace_record {
unsigned long ip;
void persistent_ram_free_old(struct persistent_ram_zone *prz);
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
char *str, size_t len);
+#ifdef CONFIG_PSTORE_RAM_ANNOTATION_APPEND
+__printf(1, 2) int persistent_ram_annotation_append(const char *fmt, ...);
+void persistent_ram_annotation_merge(struct persistent_ram_zone *prz);
+#else
+static inline __printf(1, 2) void persistent_ram_annotation_append(
+ const char *fmt, ...) { };
+static inline void persistent_ram_annotation_merge(
+ struct persistent_ram_zone *prz) { };
+#endif
void ramoops_console_write_buf(const char *buf, size_t size);
unsigned long console_size;
unsigned long ftrace_size;
unsigned long pmsg_size;
+ unsigned long annotate_size;
int dump_oops;
u32 flags;
struct persistent_ram_ecc_info ecc_info;
.console_size = SZ_512K,
.ftrace_size = SZ_512K,
.pmsg_size = SZ_512K,
+ .annotate_size = SZ_2K,
#endif
.dump_oops = 1,
};