config PSTORE tristate "Persistent store support" default n help This option enables generic access to platform level persistent storage via "pstore" filesystem that can be mounted as /dev/pstore. Only useful if you have a platform level driver that registers with pstore to provide the data, so you probably should just go say "Y" (or "M") to a platform specific persistent store driver (e.g. ACPI_APEI on X86) which will select this for you. If you don't have a platform persistent store driver, say N.
这里Only useful说的就是开启pstore必须要提供一个persistent store driver,比如ACPI_APEI on X86,这个就是最初的non-volatile storage driver,代码路径在:drivers/acpi/apei/erst.c。
而现在取代的基本都是ramoops driver,也就是CONFIG_PSTORE_RAM:
config PSTORE_RAM tristate "Log panic/oops to a RAM buffer" depends on PSTORE depends on HAS_IOMEM depends on HAVE_MEMBLOCK select REED_SOLOMON select REED_SOLOMON_ENC8 select REED_SOLOMON_DEC8 help This enables panic and oops messages to be logged to a circular buffer in RAM where it can be read back at some later point.
Note that for historical reasons, the module will be named "ramoops.ko".
For more information, see Documentation/ramoops.txt.
config PSTORE_PMSG bool "Log user space messages" depends on PSTORE help When the option is enabled, pstore will export a character interface /dev/pmsg0 to log user space messages. On reboot data can be retrieved from /sys/fs/pstore/pmsg-ramoops-[ID].
If unsure, say N.
下面主要看下这3个宏相关代码。
pstore文件系统位置在:
xxx:/ # ls /sys/fs/pstore console-ramoops-0 dmesg-ramoops-0
/* * Make a regular file in the root directory of our file system. * Load it up with "size" bytes of data from "buf". * Set the mtime & ctime to the date that this record was originally stored. */ intpstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, char *data, bool compressed, size_t size, struct timespec time, struct pstore_info *psi) { ... switch (type) { case PSTORE_TYPE_DMESG: //tj: crash log scnprintf(name, sizeof(name), "dmesg-%s-%lld%s", psname, id, compressed ? ".enc.z" : ""); break; case PSTORE_TYPE_CONSOLE: //tj: all kernel messages scnprintf(name, sizeof(name), "console-%s-%lld", psname, id); break;
/* * platform specific persistent storage driver registers with * us here. If pstore is already mounted, call the platform * read function right away to populate the file system. If not * then the pstore mount code will call us later to fill out * the file system. */ intpstore_register(struct pstore_info *psi) { structmodule *owner = psi->owner;
if (backend && strcmp(backend, psi->name)) return -EPERM;
spin_lock(&pstore_lock); if (psinfo) { spin_unlock(&pstore_lock); return -EBUSY; }
if (!psi->write) psi->write = pstore_write_compat; if (!psi->write_buf_user) psi->write_buf_user = pstore_write_buf_user_compat; psinfo = psi; mutex_init(&psinfo->read_mutex); spin_unlock(&pstore_lock); ... /* * Update the module parameter backend, so it is visible * through /sys/module/pstore/parameters/backend */ backend = psi->name;
module_put(owner);
pr_info("Registered %s as persistent store backend\n", psi->name);
/* * pstore_lock just protects "psinfo" during * calls to pstore_register() */ staticDEFINE_SPINLOCK(pstore_lock); structpstore_info *psinfo;
/* * Read all the records from the persistent store. Create * files in our filesystem. Don't warn about -EEXIST errors * when we are re-scanning the backing store looking to add new * error records. */ voidpstore_get_records(int quiet) { structpstore_info *psi = psinfo; //tj: global psinfo ... mutex_lock(&psi->read_mutex); if (psi->open && psi->open(psi)) goto out;
while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, &ecc_notice_size, psi)) > 0) { if (compressed && (type == PSTORE_TYPE_DMESG)) { if (big_oops_buf) unzipped_len = pstore_decompress(buf, big_oops_buf, size, big_oops_buf_sz);
if (unzipped_len > 0) { if (ecc_notice_size) memcpy(big_oops_buf + unzipped_len, buf + size, ecc_notice_size); kfree(buf); buf = big_oops_buf; size = unzipped_len; compressed = false; } else { pr_err("decompression failed;returned %d\n", unzipped_len); compressed = true; } } rc = pstore_mkfile(type, psi->name, id, count, buf, compressed, size + ecc_notice_size, time, psi); if (unzipped_len < 0) { /* Free buffer other than big oops */ kfree(buf); buf = NULL; } else unzipped_len = -1; if (rc && (rc != -EEXIST || !quiet)) failed++; } if (psi->close) psi->close(psi); out: mutex_unlock(&psi->read_mutex);
if needed,call pstore_decompress解压然后创建pstore文件by vfs接口pstore_mkfile。
pstore注册接下来是按类别分别注册:
if (psi->flags & PSTORE_FLAGS_DMESG) pstore_register_kmsg(); if (psi->flags & PSTORE_FLAGS_CONSOLE) pstore_register_console(); if (psi->flags & PSTORE_FLAGS_FTRACE) pstore_register_ftrace(); if (psi->flags & PSTORE_FLAGS_PMSG) pstore_register_pmsg();
/* * Register with kmsg_dump to save last part of console log on panic. */ staticvoidpstore_register_kmsg(void) { kmsg_dump_register(&pstore_dumper); }
pstore_dump最终会call backend的write,直接用全局psinfo。
/* * callback from kmsg_dump. (s2,l2) has the most recently * written bytes, older bytes are in (s1,l1). Save as much * as we can from the end of the buffer. */ staticvoidpstore_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) { ... ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, oopscount, compressed, total_len, psinfo);
kmsg_dump_register是内核一种增加log dumper方法,called when kernel oopses or panic。
staticLIST_HEAD(dump_list);
/** * kmsg_dump_register - register a kernel log dumper. * @dumper: pointer to the kmsg_dumper structure * * Adds a kernel log dumper to the system. The dump callback in the * structure will be called when the kernel oopses or panics and must be * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise. */ intkmsg_dump_register(struct kmsg_dumper *dumper) {
/** * kmsg_dump - dump kernel log to kernel message dumpers. * @reason: the reason (oops, panic etc) for dumping * * Call each of the registered dumper's dump() callback, which can * retrieve the kmsg records with kmsg_dump_get_line() or * kmsg_dump_get_buffer(). */ voidkmsg_dump(enum kmsg_dump_reason reason) { list_for_each_entry_rcu(dumper, &dump_list, list) { ... /* invoke dumper which will iterate over records */ dumper->dump(dumper, reason); //tj: call pstore_dump
such as panic:
/** * panic - halt the system * @fmt: The text string to print * * Display a message, then perform cleanups. * * This function never returns. */ voidpanic(constchar *fmt, ...) { ... /* Call flush even twice. It tries harder with a single online CPU */ printk_nmi_flush_on_panic(); kmsg_dump(KMSG_DUMP_PANIC);
pstore RAM backend是通过persistent ram(ram_core.c)来处理,这个persist ram来源Android, mark to check later.
commit cddb8751c80348df75149f44fc3bf38d3dd1f3e6 Author: Anton Vorontsov <anton.vorontsov@linaro.org> Date: Thu May 17 00:15:08 2012 -0700
staging: android: persistent_ram: Move to fs/pstore/ram_core.c This is a first step for adding ECC support for pstore RAM backend: we will use the persistent_ram routines, kindly provided by Google. Basically, persistent_ram is a set of helper routines to deal with the [optionally] ECC-protected persistent ram regions. A bit of Makefile, Kconfig and header files adjustments were needed because of the move. Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org> Acked-by: Kees Cook <keescook@chromium.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>