This is the 4th version of the patch set. In this patchset we aim to add pstore multi-backend support then user can register more than one pstore backend.
Changes in v4: - Replace all rcu_read_lock with mutex - Move bif_oops_buf, max_compressed_size and pstore_dumper into pstore_info_list - add a helper to do "is this name in the list" and a helper to do "is this backend loaded" - make comments in pstore_(un)register clearer - return the max_seen ret or the first negative err in write_pmsg() - add a /sys/module entry for the list of backends, comma separated - Link to v3: https://lore.kernel.org/all/20230928024244.257687-1-xiangzao@linux.alibaba.c...
Changes in v3: - Fix ftrace.c build error - Link to v2: https://lore.kernel.org/all/20240205122852.7069-1-xiangzao@linux.alibaba.com...
Changes in v2: - pstore.backend no longer acts as "registered backend", but "backends eligible for registration". - drop subdir since it will break user space - drop tty frontend since I haven't yet devised a satisfactory implementation strategy - Link to v1: https://lore.kernel.org/all/20230928024244.257687-1-xiangzao@linux.alibaba.c...
Yuanhe Shu (4): pstore: add multi-backend support pstore: add a /sys/module entry for loaded backends Documentation: adjust pstore backend related document tools/testing: adjust pstore backend related selftest
Documentation/ABI/testing/pstore | 8 +- .../admin-guide/kernel-parameters.txt | 4 +- fs/pstore/ftrace.c | 27 +- fs/pstore/inode.c | 57 +++- fs/pstore/internal.h | 5 +- fs/pstore/platform.c | 274 ++++++++++++------ fs/pstore/pmsg.c | 27 +- include/linux/pstore.h | 23 ++ tools/testing/selftests/pstore/common_tests | 8 +- .../selftests/pstore/pstore_post_reboot_tests | 67 +++-- tools/testing/selftests/pstore/pstore_tests | 2 +- 11 files changed, 358 insertions(+), 144 deletions(-)
Currently, pstore supports only one backend open at a time. Specifically, due to the global variable "psinfo", pstore only accepts the first registered backend. If a new backend wants to register later, pstore will simply reject it and return an error. This design forced us to close existing backend in order to use the new ones.
To enable pstore to support multiple backends, "psinfo" is replaced by "psinfo_list", a list that holds multiple "psinfo". If multiple backends are registered with the same frontend, the frontend is reused.
User can specify multiple backends that are allowed to be registered by module parameter "pstore.backend=" separated by commas or "all" to enable all available backends. If no pstore.backend was specified, pstore would accept the first registered backend which is the same as before.
Signed-off-by: Yuanhe Shu xiangzao@linux.alibaba.com Signed-off-by: Xingrui Yi yixingrui@linux.alibaba.com --- fs/pstore/ftrace.c | 27 +++- fs/pstore/inode.c | 19 ++- fs/pstore/internal.h | 4 +- fs/pstore/platform.c | 279 +++++++++++++++++++++++++++-------------- fs/pstore/pmsg.c | 27 +++- include/linux/pstore.h | 23 ++++ 6 files changed, 271 insertions(+), 108 deletions(-)
diff --git a/fs/pstore/ftrace.c b/fs/pstore/ftrace.c index 776cae20af4e..a4fd0aa0efb5 100644 --- a/fs/pstore/ftrace.c +++ b/fs/pstore/ftrace.c @@ -23,10 +23,11 @@ /* This doesn't need to be atomic: speed is chosen over correctness here. */ static u64 pstore_ftrace_stamp;
-static void notrace pstore_ftrace_call(unsigned long ip, +static void notrace pstore_do_ftrace(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, - struct ftrace_regs *fregs) + struct ftrace_regs *fregs, + struct pstore_info *psinfo) { int bit; unsigned long flags; @@ -57,6 +58,20 @@ static void notrace pstore_ftrace_call(unsigned long ip, ftrace_test_recursion_unlock(bit); }
+static void notrace pstore_ftrace_call(unsigned long ip, + unsigned long parent_ip, + struct ftrace_ops *op, + struct ftrace_regs *fregs) +{ + struct pstore_info_list *entry; + + mutex_lock(&psback_lock); + list_for_each_entry(entry, &psback->list_entry, list) + if (entry->psi->flags & PSTORE_FLAGS_FTRACE) + pstore_do_ftrace(ip, parent_ip, op, fregs, entry->psi); + mutex_unlock(&psback_lock); +} + static struct ftrace_ops pstore_ftrace_ops __read_mostly = { .func = pstore_ftrace_call, }; @@ -131,8 +146,12 @@ MODULE_PARM_DESC(record_ftrace,
void pstore_register_ftrace(void) { - if (!psinfo->write) - return; + struct pstore_info_list *entry; + + list_for_each_entry(entry, &psback->list_entry, list) + if (entry->psi->flags & PSTORE_FLAGS_FTRACE) + if (!entry->psi->write) + return;
pstore_ftrace_dir = debugfs_create_dir("pstore", NULL);
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index d0d9bfdad30c..bee71c7da995 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -285,7 +285,7 @@ static const struct super_operations pstore_ops = { .show_options = pstore_show_options, };
-static struct dentry *psinfo_lock_root(void) +static struct dentry *psinfo_lock_root(struct pstore_info *psinfo) { struct dentry *root;
@@ -309,7 +309,7 @@ int pstore_put_backend_records(struct pstore_info *psi) struct dentry *root; int rc = 0;
- root = psinfo_lock_root(); + root = psinfo_lock_root(psi); if (!root) return 0;
@@ -398,21 +398,22 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record) * when we are re-scanning the backing store looking to add new * error records. */ -void pstore_get_records(int quiet) +void pstore_get_records(struct pstore_info *psi, int quiet) { struct dentry *root;
- root = psinfo_lock_root(); + root = psinfo_lock_root(psi); if (!root) return;
- pstore_get_backend_records(psinfo, root, quiet); + pstore_get_backend_records(psi, root, quiet); inode_unlock(d_inode(root)); }
static int pstore_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; + struct pstore_info_list *entry;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_SIZE; @@ -437,7 +438,13 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent) scoped_guard(mutex, &pstore_sb_lock) pstore_sb = sb;
- pstore_get_records(0); + if (!psback) + return 0; + + mutex_lock(&psback_lock); + list_for_each_entry(entry, &psback->list_entry, list) + pstore_get_records(entry->psi, 0); + mutex_unlock(&psback_lock);
return 0; } diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 801d6c0b170c..4b1c7ba27052 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -33,10 +33,10 @@ static inline void pstore_register_pmsg(void) {} static inline void pstore_unregister_pmsg(void) {} #endif
-extern struct pstore_info *psinfo; +extern struct pstore_backends *psback;
extern void pstore_set_kmsg_bytes(int); -extern void pstore_get_records(int); +extern void pstore_get_records(struct pstore_info *psi, int quiet); extern void pstore_get_backend_records(struct pstore_info *psi, struct dentry *root, int quiet); extern int pstore_put_backend_records(struct pstore_info *psi); diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 03425928d2fb..a1742b39fb88 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -62,12 +62,12 @@ static void pstore_dowork(struct work_struct *); static DECLARE_WORK(pstore_work, pstore_dowork);
/* - * psinfo_lock protects "psinfo" during calls to + * psback_lock protects "psback" during calls to * pstore_register(), pstore_unregister(), and * the filesystem mount/unmount routines. */ -static DEFINE_MUTEX(psinfo_lock); -struct pstore_info *psinfo; +DEFINE_MUTEX(psback_lock); +struct pstore_backends *psback;
static char *backend; module_param(backend, charp, 0444); @@ -104,9 +104,6 @@ static void *compress_workspace; */ #define DMESG_COMP_PERCENT 60
-static char *big_oops_buf; -static size_t max_compressed_size; - void pstore_set_kmsg_bytes(int bytes) { kmsg_bytes = bytes; @@ -201,7 +198,7 @@ static int pstore_compress(const void *in, void *out, return zstream.total_out; }
-static void allocate_buf_for_compression(void) +static void allocate_buf_for_compression(struct pstore_info_list *entry) { size_t compressed_size; char *buf; @@ -224,11 +221,11 @@ static void allocate_buf_for_compression(void) * uncompressed record size, since any record that would be expanded by * compression is just stored uncompressed. */ - compressed_size = (psinfo->bufsize * 100) / DMESG_COMP_PERCENT; + compressed_size = (entry->psi->bufsize * 100) / DMESG_COMP_PERCENT; buf = kvzalloc(compressed_size, GFP_KERNEL); if (!buf) { pr_err("Failed %zu byte compression buffer allocation for: %s\n", - psinfo->bufsize, compress); + entry->psi->bufsize, compress); return; }
@@ -241,22 +238,22 @@ static void allocate_buf_for_compression(void) }
/* A non-NULL big_oops_buf indicates compression is available. */ - big_oops_buf = buf; - max_compressed_size = compressed_size; + entry->big_oops_buf = buf; + entry->max_compressed_size = compressed_size;
pr_info("Using crash dump compression: %s\n", compress); }
-static void free_buf_for_compression(void) +static void free_buf_for_compression(struct pstore_info_list *entry) { if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress_workspace) { vfree(compress_workspace); compress_workspace = NULL; }
- kvfree(big_oops_buf); - big_oops_buf = NULL; - max_compressed_size = 0; + kvfree(entry->big_oops_buf); + entry->big_oops_buf = NULL; + entry->max_compressed_size = 0; }
void pstore_record_init(struct pstore_record *record, @@ -278,6 +275,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) { struct kmsg_dump_iter iter; + struct pstore_info_list *entry; unsigned long total = 0; const char *why; unsigned int part = 1; @@ -285,16 +283,18 @@ static void pstore_dump(struct kmsg_dumper *dumper, int saved_ret = 0; int ret;
+ entry = container_of(dumper, struct pstore_info_list, pstore_dumper); + why = kmsg_dump_reason_str(reason);
if (pstore_cannot_block_path(reason)) { - if (!spin_trylock_irqsave(&psinfo->buf_lock, flags)) { + if (!spin_trylock_irqsave(&entry->psi->buf_lock, flags)) { pr_err("dump skipped in %s path because of concurrent dump\n", in_nmi() ? "NMI" : why); return; } } else { - spin_lock_irqsave(&psinfo->buf_lock, flags); + spin_lock_irqsave(&entry->psi->buf_lock, flags); }
kmsg_dump_rewind(&iter); @@ -308,15 +308,15 @@ static void pstore_dump(struct kmsg_dumper *dumper, size_t dump_size; struct pstore_record record;
- pstore_record_init(&record, psinfo); + pstore_record_init(&record, entry->psi); record.type = PSTORE_TYPE_DMESG; record.count = oopscount; record.reason = reason; record.part = part; - record.buf = psinfo->buf; + record.buf = entry->psi->buf;
- dst = big_oops_buf ?: psinfo->buf; - dst_size = max_compressed_size ?: psinfo->bufsize; + dst = entry->big_oops_buf ?: entry->psi->buf; + dst_size = entry->max_compressed_size ?: entry->psi->bufsize;
/* Write dump header. */ header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, @@ -328,10 +328,10 @@ static void pstore_dump(struct kmsg_dumper *dumper, dst_size, &dump_size)) break;
- if (big_oops_buf) { - zipped_len = pstore_compress(dst, psinfo->buf, + if (entry->big_oops_buf) { + zipped_len = pstore_compress(dst, entry->psi->buf, header_size + dump_size, - psinfo->bufsize); + entry->psi->bufsize);
if (zipped_len > 0) { record.compressed = true; @@ -344,14 +344,14 @@ static void pstore_dump(struct kmsg_dumper *dumper, * of the uncompressed data as possible into * the pstore record, and discard the rest. */ - record.size = psinfo->bufsize; - memcpy(psinfo->buf, dst, psinfo->bufsize); + record.size = entry->psi->bufsize; + memcpy(entry->psi->buf, dst, entry->psi->bufsize); } } else { record.size = header_size + dump_size; }
- ret = psinfo->write(&record); + ret = entry->psi->write(&record); if (ret == 0 && reason == KMSG_DUMP_OOPS) { pstore_new_entry = 1; pstore_timer_kick(); @@ -364,39 +364,33 @@ static void pstore_dump(struct kmsg_dumper *dumper, total += record.size; part++; } - spin_unlock_irqrestore(&psinfo->buf_lock, flags); + spin_unlock_irqrestore(&entry->psi->buf_lock, flags);
if (saved_ret) { - pr_err_once("backend (%s) writing error (%d)\n", psinfo->name, + pr_err_once("backend (%s) writing error (%d)\n", entry->psi->name, saved_ret); } }
-static struct kmsg_dumper pstore_dumper = { - .dump = pstore_dump, -}; - /* * Register with kmsg_dump to save last part of console log on panic. */ -static void pstore_register_kmsg(void) +static void pstore_register_kmsg(struct kmsg_dumper *pstore_dumper) { - kmsg_dump_register(&pstore_dumper); + kmsg_dump_register(pstore_dumper); }
-static void pstore_unregister_kmsg(void) +static void pstore_unregister_kmsg(struct kmsg_dumper *pstore_dumper) { - kmsg_dump_unregister(&pstore_dumper); + kmsg_dump_unregister(pstore_dumper); }
#ifdef CONFIG_PSTORE_CONSOLE -static void pstore_console_write(struct console *con, const char *s, unsigned c) +static void pstore_console_do_write(struct console *con, const char *s, + unsigned c, struct pstore_info *psinfo) { struct pstore_record record;
- if (!c) - return; - pstore_record_init(&record, psinfo); record.type = PSTORE_TYPE_CONSOLE;
@@ -405,6 +399,21 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) psinfo->write(&record); }
+static void pstore_console_write(struct console *con, const char *s, + unsigned int c) +{ + struct pstore_info_list *entry; + + if (!c) + return; + + mutex_lock(&psback_lock); + list_for_each_entry(entry, &psback->list_entry, list) + if (entry->psi->flags & PSTORE_FLAGS_CONSOLE) + pstore_console_do_write(con, s, c, entry->psi); + mutex_unlock(&psback_lock); +} + static struct console pstore_console = { .write = pstore_console_write, .index = -1, @@ -413,7 +422,7 @@ static struct console pstore_console = { static void pstore_register_console(void) { /* Show which backend is going to get console writes. */ - strscpy(pstore_console.name, psinfo->name, + strscpy(pstore_console.name, "pstore console", sizeof(pstore_console.name)); /* * Always initialize flags here since prior unregister_console() @@ -455,6 +464,33 @@ static int pstore_write_user_compat(struct pstore_record *record, return unlikely(ret < 0) ? ret : record->size; }
+static bool is_backend_enabled(char *backend, const char *name) +{ + char *sep, *backend_name; + + if (!backend || strcmp(backend, "all") == 0) + return true; + else { + sep = kstrdup(backend, GFP_KERNEL); + while ((backend_name = strsep(&sep, ",")) != NULL) { + if (strcmp(name, backend_name) == 0) + return true; + } + } + return false; +} + +static bool is_backend_loaded(const char *name) +{ + struct pstore_info_list *entry; + + list_for_each_entry(entry, &psback->list_entry, list) + if (strcmp(entry->psi->name, name) == 0) + return true; + + return false; +} + /* * platform specific persistent storage driver registers with * us here. If pstore is already mounted, call the platform @@ -464,12 +500,14 @@ static int pstore_write_user_compat(struct pstore_record *record, */ int pstore_register(struct pstore_info *psi) { + struct pstore_info_list *newpsi; char *new_backend;
- if (backend && strcmp(backend, psi->name)) { - pr_warn("backend '%s' already in use: ignoring '%s'\n", - backend, psi->name); - return -EBUSY; + /* backend has to be in pstore.backend for going on registering */ + if (!is_backend_enabled(backend, psi->name)) { + pr_warn("backend '%s' ignored: not present in " + "pstore.backend=...\n", psi->name); + return -EINVAL; }
/* Sanity check flags. */ @@ -486,80 +524,125 @@ int pstore_register(struct pstore_info *psi) return -EINVAL; }
- new_backend = kstrdup(psi->name, GFP_KERNEL); - if (!new_backend) - return -ENOMEM; - - mutex_lock(&psinfo_lock); - if (psinfo) { - pr_warn("backend '%s' already loaded: ignoring '%s'\n", - psinfo->name, psi->name); - mutex_unlock(&psinfo_lock); - kfree(new_backend); - return -EBUSY; + mutex_lock(&psback_lock); + + /* + * If no backend specified, first come first served to + * maintain backward compatibility + */ + if (!backend) { + new_backend = kstrdup(psi->name, GFP_KERNEL); + if (!new_backend) { + mutex_unlock(&psback_lock); + return -ENOMEM; + } + pr_warn("pstore.backend=... not specified, " + "registering first available: '%s'\n", + psi->name); + } + + if (psback) { + if (is_backend_loaded(psi->name)) { + pr_warn("backend '%s' already loaded; " + "not loading it again\n", psi->name); + mutex_unlock(&psback_lock); + return -EPERM; + } + } else { + psback = kzalloc(sizeof(*psback), GFP_KERNEL); + if (!psback) { + mutex_unlock(&psback_lock); + return -ENOMEM; + } + INIT_LIST_HEAD(&psback->list_entry); }
if (!psi->write_user) psi->write_user = pstore_write_user_compat; - psinfo = psi; - mutex_init(&psinfo->read_mutex); - spin_lock_init(&psinfo->buf_lock); + + newpsi = kzalloc(sizeof(*newpsi), GFP_KERNEL); + if (!newpsi) { + mutex_unlock(&psback_lock); + return -EPERM; + } + newpsi->psi = psi; + + mutex_init(&psi->read_mutex); + spin_lock_init(&psi->buf_lock);
if (psi->flags & PSTORE_FLAGS_DMESG) - allocate_buf_for_compression(); + allocate_buf_for_compression(newpsi);
- pstore_get_records(0); + pstore_get_records(psi, 0); + + list_add(&newpsi->list, &psback->list_entry);
if (psi->flags & PSTORE_FLAGS_DMESG) { - pstore_dumper.max_reason = psinfo->max_reason; - pstore_register_kmsg(); + newpsi->pstore_dumper.dump = pstore_dump; + newpsi->pstore_dumper.max_reason = psi->max_reason; + pstore_register_kmsg(&newpsi->pstore_dumper); } - if (psi->flags & PSTORE_FLAGS_CONSOLE) + if (psi->flags & PSTORE_FLAGS_CONSOLE + && !psback->front_cnt[PSTORE_TYPE_CONSOLE]++) pstore_register_console(); - if (psi->flags & PSTORE_FLAGS_FTRACE) + if (psi->flags & PSTORE_FLAGS_FTRACE && + !psback->front_cnt[PSTORE_TYPE_FTRACE]++) pstore_register_ftrace(); - if (psi->flags & PSTORE_FLAGS_PMSG) + if (psi->flags & PSTORE_FLAGS_PMSG && + !psback->front_cnt[PSTORE_TYPE_PMSG]++) pstore_register_pmsg();
/* Start watching for new records, if desired. */ pstore_timer_kick();
/* - * Update the module parameter backend, so it is visible + * When module parameter backend is not specified, + * update the module parameter backend, so it is visible * through /sys/module/pstore/parameters/backend */ - backend = new_backend; + if (!backend) + backend = new_backend;
pr_info("Registered %s as persistent store backend\n", psi->name);
- mutex_unlock(&psinfo_lock); + mutex_unlock(&psback_lock); return 0; } EXPORT_SYMBOL_GPL(pstore_register);
void pstore_unregister(struct pstore_info *psi) { + struct pstore_info_list *entry, *tmp; + + pr_info("Unregistering %s as persistent store backend\n", psi->name); + /* It's okay to unregister nothing. */ if (!psi) return;
- mutex_lock(&psinfo_lock); + mutex_lock(&psback_lock);
- /* Only one backend can be registered at a time. */ - if (WARN_ON(psi != psinfo)) { - mutex_unlock(&psinfo_lock); + /* Can not unregister an unloaded backend*/ + if (WARN_ON(!is_backend_loaded(psi->name))) { + mutex_unlock(&psback_lock); return; }
/* Unregister all callbacks. */ - if (psi->flags & PSTORE_FLAGS_PMSG) + if (psi->flags & PSTORE_FLAGS_PMSG && + !--psback->front_cnt[PSTORE_TYPE_PMSG]) pstore_unregister_pmsg(); - if (psi->flags & PSTORE_FLAGS_FTRACE) + if (psi->flags & PSTORE_FLAGS_FTRACE && + !--psback->front_cnt[PSTORE_TYPE_FTRACE]) pstore_unregister_ftrace(); - if (psi->flags & PSTORE_FLAGS_CONSOLE) + if (psi->flags & PSTORE_FLAGS_CONSOLE && + !--psback->front_cnt[PSTORE_TYPE_CONSOLE]) pstore_unregister_console(); - if (psi->flags & PSTORE_FLAGS_DMESG) - pstore_unregister_kmsg(); + list_for_each_entry(entry, &psback->list_entry, list) { + if (entry->psi == psi) + if (psi->flags & PSTORE_FLAGS_DMESG) + pstore_unregister_kmsg(&entry->pstore_dumper); + }
/* Stop timer and make sure all work has finished. */ del_timer_sync(&pstore_timer); @@ -568,19 +651,28 @@ void pstore_unregister(struct pstore_info *psi) /* Remove all backend records from filesystem tree. */ pstore_put_backend_records(psi);
- free_buf_for_compression(); + list_for_each_entry_safe(entry, tmp, &psback->list_entry, list) { + if (entry->psi == psi) { + list_del(&entry->list); + free_buf_for_compression(entry); + kfree(entry); + break; + } + }
- psinfo = NULL; - kfree(backend); - backend = NULL; + if (list_empty(&psback->list_entry)) { + kfree(psback); + psback = NULL; + }
pr_info("Unregistered %s as persistent store backend\n", psi->name); - mutex_unlock(&psinfo_lock); + mutex_unlock(&psback_lock); } EXPORT_SYMBOL_GPL(pstore_unregister);
static void decompress_record(struct pstore_record *record, - struct z_stream_s *zstream) + struct z_stream_s *zstream, + struct pstore_info *psinfo) { int ret; int unzipped_len; @@ -697,7 +789,7 @@ void pstore_get_backend_records(struct pstore_info *psi, break; }
- decompress_record(record, &zstream); + decompress_record(record, &zstream, psi); rc = pstore_mkfile(root, record); if (rc) { /* pstore_mkfile() did not take record, so free it. */ @@ -729,7 +821,12 @@ void pstore_get_backend_records(struct pstore_info *psi,
static void pstore_dowork(struct work_struct *work) { - pstore_get_records(1); + struct pstore_info_list *entry; + + mutex_lock(&psback_lock); + list_for_each_entry(entry, &psback->list_entry, list) + pstore_get_records(entry->psi, 1); + mutex_unlock(&psback_lock); }
static void pstore_timefunc(struct timer_list *unused) @@ -744,13 +841,7 @@ static void pstore_timefunc(struct timer_list *unused)
static int __init pstore_init(void) { - int ret; - - ret = pstore_init_fs(); - if (ret) - free_buf_for_compression(); - - return ret; + return pstore_init_fs(); } late_initcall(pstore_init);
diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c index 55f139afa327..facde6a15fc4 100644 --- a/fs/pstore/pmsg.c +++ b/fs/pstore/pmsg.c @@ -11,8 +11,9 @@
static DEFINE_MUTEX(pmsg_lock);
-static ssize_t write_pmsg(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t do_write_pmsg(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, + struct pstore_info *psinfo) { struct pstore_record record; int ret; @@ -34,6 +35,28 @@ static ssize_t write_pmsg(struct file *file, const char __user *buf, return ret ? ret : count; }
+static ssize_t write_pmsg(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret, written = 0, err = 0; + struct pstore_info_list *entry; + + mutex_lock(&psback_lock); + list_for_each_entry(entry, &psback->list_entry, list) { + if (entry->psi->flags & PSTORE_FLAGS_PMSG) { + ret = do_write_pmsg(file, buf, count, + ppos, entry->psi); + /* Return the first negative value or max_seen ret */ + if (!err && ret < 0) + err = ret; + written = ret > written ? ret : written; + } + } + mutex_unlock(&psback_lock); + + return err ? err : written; +} + static const struct file_operations pmsg_fops = { .owner = THIS_MODULE, .llseek = noop_llseek, diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 638507a3c8ff..f57ce9f50b2b 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -201,6 +201,29 @@ struct pstore_info { int (*erase)(struct pstore_record *record); };
+/* Supported multibackends */ +extern struct mutex psback_lock; + +struct pstore_info_list { + struct pstore_info *psi; + char *big_oops_buf; + size_t max_compressed_size; + struct kmsg_dumper pstore_dumper; + struct list_head list; +}; + +/** + * struct pstore_backends - management of pstore backends + * @front_cnt: count of each enabled frontend + * @list_entry: entry of pstore backend driver information list + * + */ + +struct pstore_backends { + int front_cnt[PSTORE_TYPE_MAX]; + struct list_head list_entry; +}; + /* Supported frontends */ #define PSTORE_FLAGS_DMESG BIT(0) #define PSTORE_FLAGS_CONSOLE BIT(1)
Introduce a /sys/module entry for loaded pstore backends which provide users and testcases with a standarized interface to retrieve information on which pstore backends are currently registered.
Signed-off-by: Yuanhe Shu xiangzao@linux.alibaba.com --- fs/pstore/inode.c | 38 ++++++++++++++++++++++++++++++++++++++ fs/pstore/internal.h | 1 + fs/pstore/platform.c | 9 ++++++++- 3 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index bee71c7da995..5800fa4abfce 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -6,6 +6,7 @@ */
#include <linux/module.h> +#include <linux/kobject.h> #include <linux/fs.h> #include <linux/fsnotify.h> #include <linux/pagemap.h> @@ -491,6 +492,43 @@ int __init pstore_init_fs(void) return err; }
+static ssize_t loaded_backend_show(struct kobject *k, + struct kobj_attribute *attr, char *buf) +{ + struct pstore_info_list *entry; + char *old, *loaded_backend = NULL; + + mutex_lock(&psback_lock); + list_for_each_entry(entry, &psback->list_entry, list) + if (!loaded_backend) + loaded_backend = kstrdup(entry->psi->name, GFP_KERNEL); + else { + old = loaded_backend; + loaded_backend = kasprintf(GFP_KERNEL, "%s,%s", + old, entry->psi->name); + kfree(old); + } + mutex_unlock(&psback_lock); + + return sprintf(buf, "%s\n", loaded_backend); +} + +static struct kobj_attribute backend_attribute = + __ATTR(loaded_backend, 0444, loaded_backend_show, NULL); + +int __init pstore_init_entry(void) +{ + int err = 0; + struct kobject *pstore_kobj; + + pstore_kobj = kset_find_obj(module_kset, "pstore"); + if (pstore_kobj) { + err = sysfs_create_file(pstore_kobj, &backend_attribute.attr); + kobject_put(pstore_kobj); + } + return err; +} + void __exit pstore_exit_fs(void) { unregister_filesystem(&pstore_fs_type); diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 4b1c7ba27052..ffc86b04c5cc 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -47,6 +47,7 @@ extern void pstore_record_init(struct pstore_record *record,
/* Called during pstore init/exit. */ int __init pstore_init_fs(void); +int __init pstore_init_entry(void); void __exit pstore_exit_fs(void);
#endif diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index a1742b39fb88..5c10a546cdf0 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -841,7 +841,14 @@ static void pstore_timefunc(struct timer_list *unused)
static int __init pstore_init(void) { - return pstore_init_fs(); + int ret; + + ret = pstore_init_fs(); + if (ret) + return ret; + + ret = pstore_init_entry(); + return ret; } late_initcall(pstore_init);
Pstore now supports multiple backends, adjust related document.
Signed-off-by: Yuanhe Shu xiangzao@linux.alibaba.com --- Documentation/ABI/testing/pstore | 8 ++++---- Documentation/admin-guide/kernel-parameters.txt | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/Documentation/ABI/testing/pstore b/Documentation/ABI/testing/pstore index d3cff4a7ee10..b3ae01237b54 100644 --- a/Documentation/ABI/testing/pstore +++ b/Documentation/ABI/testing/pstore @@ -41,7 +41,7 @@ Description: Generic interface to platform dependent persistent storage. persistent storage until at least this amount is reached. Default is 10 Kbytes.
- Pstore only supports one backend at a time. If multiple - backends are available, the preferred backend may be - set by passing the pstore.backend= argument to the kernel at - boot time. + Pstore supports multiple backends at a time. If multiple + backends are available, the registrable backends may be + set by passing the pstore.backend=argument1,argument2... + or pstore.backend= all to the kernel at boot time. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 31b3a25680d0..a8a109b822a9 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4748,7 +4748,9 @@ [HW,MOUSE] Controls Logitech smartscroll autorepeat. 0 = disabled, 1 = enabled (default).
- pstore.backend= Specify the name of the pstore backend to use + pstore.backend=backend1,...,backendN + Specify the names of the pstore backends to use + or =all for all available backends
pti= [X86-64] Control Page Table Isolation of user and kernel address spaces. Disabling this feature
Pstore now supports multiple backends, the module parameter pstore.backend varies from 'registered backend' to 'backends that are allowed to register' and a new entry /sys/module/pstore/loaded_backend is added to show which pstore backends are loaded at present. Adjust selftests to match the change.
Signed-off-by: Yuanhe Shu xiangzao@linux.alibaba.com --- tools/testing/selftests/pstore/common_tests | 8 +-- .../selftests/pstore/pstore_post_reboot_tests | 67 ++++++++++--------- tools/testing/selftests/pstore/pstore_tests | 2 +- 3 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/tools/testing/selftests/pstore/common_tests b/tools/testing/selftests/pstore/common_tests index 4509f0cc9c91..db717f656a73 100755 --- a/tools/testing/selftests/pstore/common_tests +++ b/tools/testing/selftests/pstore/common_tests @@ -27,9 +27,9 @@ show_result() { # result_value }
check_files_exist() { # type of pstorefs file - if [ -e ${1}-${backend}-0 ]; then + if [ -e ${1}-${2}-0 ]; then prlog "ok" - for f in `ls ${1}-${backend}-*`; do + for f in `ls ${1}-${2}-*`; do prlog -e "\t${f}" done else @@ -74,9 +74,9 @@ prlog "=== Pstore unit tests (`basename $0`) ===" prlog "UUID="$UUID
prlog -n "Checking pstore backend is registered ... " -backend=`cat /sys/module/pstore/parameters/backend` +backends=`cat /sys/module/pstore/loaded_backend` show_result $? -prlog -e "\tbackend=${backend}" +prlog -e "\tbackends=${backends}" prlog -e "\tcmdline=`cat /proc/cmdline`" if [ $rc -ne 0 ]; then exit 1 diff --git a/tools/testing/selftests/pstore/pstore_post_reboot_tests b/tools/testing/selftests/pstore/pstore_post_reboot_tests index d6da5e86efbf..666b45bd7b87 100755 --- a/tools/testing/selftests/pstore/pstore_post_reboot_tests +++ b/tools/testing/selftests/pstore/pstore_post_reboot_tests @@ -35,46 +35,49 @@ else fi fi
+IFS=',' cd ${mount_point} +for backend in ${backends}; do + prlog -n "Checking ${backend}-dmesg files exist in pstore filesystem ... " + check_files_exist dmesg ${backend}
-prlog -n "Checking dmesg files exist in pstore filesystem ... " -check_files_exist dmesg + prlog -n "Checking ${backend}-console files exist in pstore filesystem ... " + check_files_exist console ${backend}
-prlog -n "Checking console files exist in pstore filesystem ... " -check_files_exist console + prlog -n "Checking ${backend}-pmsg files exist in pstore filesystem ... " + check_files_exist pmsg ${backend}
-prlog -n "Checking pmsg files exist in pstore filesystem ... " -check_files_exist pmsg + prlog -n "Checking ${backend}-dmesg files contain oops end marker" + grep_end_trace() { + grep -q "---[ end trace" $1 + } + files=`ls dmesg-${backend}-*` + operate_files $? "$files" grep_end_trace
-prlog -n "Checking dmesg files contain oops end marker" -grep_end_trace() { - grep -q "---[ end trace" $1 -} -files=`ls dmesg-${backend}-*` -operate_files $? "$files" grep_end_trace + prlog -n "Checking ${backend}-console file contains oops end marker ... " + grep -q "---[ end trace" console-${backend}-0 + show_result $?
-prlog -n "Checking console file contains oops end marker ... " -grep -q "---[ end trace" console-${backend}-0 -show_result $? - -prlog -n "Checking pmsg file properly keeps the content written before crash ... " -prev_uuid=`cat $TOP_DIR/prev_uuid` -if [ $? -eq 0 ]; then - nr_matched=`grep -c "$TEST_STRING_PATTERN" pmsg-${backend}-0` - if [ $nr_matched -eq 1 ]; then - grep -q "$TEST_STRING_PATTERN"$prev_uuid pmsg-${backend}-0 - show_result $? + prlog -n "Checking ${backend}-pmsg file properly keeps the content written before crash ... " + prev_uuid=`cat $TOP_DIR/prev_uuid` + if [ $? -eq 0 ]; then + nr_matched=`grep -c "$TEST_STRING_PATTERN" pmsg-${backend}-0` + if [ $nr_matched -eq 1 ]; then + grep -q "$TEST_STRING_PATTERN"$prev_uuid pmsg-${backend}-0 + show_result $? + else + prlog "FAIL" + rc=1 + fi else - prlog "FAIL" - rc=1 + prlog "FAIL" + rc=1 fi -else - prlog "FAIL" - rc=1 -fi
-prlog -n "Removing all files in pstore filesystem " -files=`ls *-${backend}-*` -operate_files $? "$files" rm + prlog -n "Removing all ${backend} files in pstore filesystem " + files=`ls *-${backend}-*` + operate_files $? "$files" rm +done +unset IFS
exit $rc diff --git a/tools/testing/selftests/pstore/pstore_tests b/tools/testing/selftests/pstore/pstore_tests index 2aa9a3852a84..f4665a8c77dc 100755 --- a/tools/testing/selftests/pstore/pstore_tests +++ b/tools/testing/selftests/pstore/pstore_tests @@ -10,7 +10,7 @@ . ./common_tests
prlog -n "Checking pstore console is registered ... " -dmesg | grep -Eq "console [(pstore|${backend})" +dmesg | grep -Eq "console [(pstore console)" show_result $?
prlog -n "Checking /dev/pmsg0 exists ... "
linux-kselftest-mirror@lists.linaro.org