From 63bcaeb59666ebb854602cb4a2e4aeffe164f7d7 Mon Sep 17 00:00:00 2001 From: Xingrui Yi Date: Thu, 4 Aug 2022 16:39:02 +0800 Subject: [PATCH 1/4] pstore: add multibackends support 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. All the backends are visible through /sys/module/pstore/parameters/backend. Signed-off-by: Xingrui Yi --- fs/pstore/ftrace.c | 26 +++-- fs/pstore/inode.c | 16 +-- fs/pstore/internal.h | 7 +- fs/pstore/platform.c | 225 ++++++++++++++++++++++++++--------------- fs/pstore/pmsg.c | 23 ++++- include/linux/pstore.h | 27 +++++ 6 files changed, 226 insertions(+), 98 deletions(-) diff --git a/fs/pstore/ftrace.c b/fs/pstore/ftrace.c index 5c0450701293..5aad3f5e5ee7 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, - unsigned long parent_ip, - struct ftrace_ops *op, - struct pt_regs *regs) +static void notrace pstore_do_ftrace(unsigned long ip, + unsigned long parent_ip, + struct ftrace_ops *op, + struct pt_regs *regs, + struct pstore_info *psinfo) { unsigned long flags; struct pstore_ftrace_record rec = {}; @@ -51,6 +52,20 @@ static void notrace pstore_ftrace_call(unsigned long ip, local_irq_restore(flags); } +static void notrace pstore_ftrace_call(unsigned long ip, + unsigned long parent_ip, + struct ftrace_ops *op, + struct pt_regs *regs) +{ + struct pstore_info_list *entry; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + if (entry->psi->flags & PSTORE_FLAGS_FTRACE) + pstore_do_ftrace(ip, parent_ip, op, regs, entry->psi); + rcu_read_unlock(); +} + static struct ftrace_ops pstore_ftrace_ops __read_mostly = { .func = pstore_ftrace_call, }; @@ -113,9 +128,6 @@ static struct dentry *pstore_ftrace_dir; void pstore_register_ftrace(void) { - if (!psinfo->write) - return; - pstore_ftrace_dir = debugfs_create_dir("pstore", NULL); debugfs_create_file("record_ftrace", 0600, pstore_ftrace_dir, NULL, diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index bbf241a431f2..cdc44aef9f7f 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -287,7 +287,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; @@ -314,7 +314,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; @@ -413,21 +413,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 pos, 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, pos); 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; @@ -453,7 +454,10 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent) pstore_sb = sb; mutex_unlock(&pstore_sb_lock); - pstore_get_records(0); + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + pstore_get_records(entry->psi, entry->index, 0); + rcu_read_unlock(); return 0; } diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 7fb219042f13..fac8e331036f 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -34,12 +34,13 @@ 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 pos, + int quiet); extern void pstore_get_backend_records(struct pstore_info *psi, - struct dentry *root, int quiet); + struct dentry *root, int quiet, int pos); extern int pstore_put_backend_records(struct pstore_info *psi); extern int pstore_mkfile(struct dentry *root, struct pstore_record *record); diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index ce03c3dbb5c3..24e56c935b5c 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -69,12 +69,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; +static DEFINE_MUTEX(psback_lock); +struct pstore_backends *psback; static char *backend; module_param(backend, charp, 0444); @@ -97,8 +97,8 @@ struct pstore_zbackend { const char *name; }; -static char *big_oops_buf; -static size_t big_oops_buf_sz; +static char *big_oops_buf[PSTORE_BACKEND_NUM]; +static size_t big_oops_buf_sz[PSTORE_BACKEND_NUM]; /* How much of the console log to snapshot */ unsigned long kmsg_bytes = PSTORE_DEFAULT_KMSG_BYTES; @@ -282,7 +282,7 @@ static int pstore_compress(const void *in, void *out, return outlen; } -static void allocate_buf_for_compression(void) +static void allocate_buf_for_compression(struct pstore_info *psinfo, int pos) { struct crypto_comp *ctx; int size; @@ -292,8 +292,8 @@ static void allocate_buf_for_compression(void) if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !zbackend) return; - /* Skip if no pstore backend yet or compression init already done. */ - if (!psinfo || tfm) + /* Skip if no pstore backend yet. */ + if (!psinfo) return; if (!crypto_has_comp(zbackend->name, 0, 0)) { @@ -325,21 +325,21 @@ static void allocate_buf_for_compression(void) /* A non-NULL big_oops_buf indicates compression is available. */ tfm = ctx; - big_oops_buf_sz = size; - big_oops_buf = buf; + big_oops_buf_sz[pos] = size; + big_oops_buf[pos] = buf; pr_info("Using crash dump compression: %s\n", zbackend->name); } -static void free_buf_for_compression(void) +static void free_buf_for_compression(int pos) { if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && tfm) { crypto_free_comp(tfm); tfm = NULL; } - kfree(big_oops_buf); - big_oops_buf = NULL; - big_oops_buf_sz = 0; + kfree(big_oops_buf[pos]); + big_oops_buf[pos] = NULL; + big_oops_buf_sz[pos] = 0; } /* @@ -349,7 +349,8 @@ static void free_buf_for_compression(void) * printk buffer which results in fetching old contents. * Copy the recent messages from big_oops_buf to psinfo->buf */ -static size_t copy_kmsg_to_buffer(int hsize, size_t len) +static size_t copy_kmsg_to_buffer(int hsize, size_t len, + struct pstore_info *psinfo, int pos) { size_t total_len; size_t diff; @@ -358,12 +359,12 @@ static size_t copy_kmsg_to_buffer(int hsize, size_t len) if (total_len > psinfo->bufsize) { diff = total_len - psinfo->bufsize + hsize; - memcpy(psinfo->buf, big_oops_buf, hsize); - memcpy(psinfo->buf + hsize, big_oops_buf + diff, + memcpy(psinfo->buf, big_oops_buf[pos], hsize); + memcpy(psinfo->buf + hsize, big_oops_buf[pos] + diff, psinfo->bufsize - hsize); total_len = psinfo->bufsize; } else - memcpy(psinfo->buf, big_oops_buf, total_len); + memcpy(psinfo->buf, big_oops_buf[pos], total_len); return total_len; } @@ -383,8 +384,8 @@ void pstore_record_init(struct pstore_record *record, * callback from kmsg_dump. Save as much as we can (up to kmsg_bytes) from the * end of the buffer. */ -static void pstore_dump(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason) +static void pstore_do_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason, struct pstore_info *psinfo, int pos) { unsigned long total = 0; const char *why; @@ -420,9 +421,9 @@ static void pstore_dump(struct kmsg_dumper *dumper, record.part = part; record.buf = psinfo->buf; - if (big_oops_buf) { - dst = big_oops_buf; - dst_size = big_oops_buf_sz; + if (big_oops_buf[pos]) { + dst = big_oops_buf[pos]; + dst_size = big_oops_buf_sz[pos]; } else { dst = psinfo->buf; dst_size = psinfo->bufsize; @@ -438,7 +439,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, dst_size, &dump_size)) break; - if (big_oops_buf) { + if (big_oops_buf[pos]) { zipped_len = pstore_compress(dst, psinfo->buf, header_size + dump_size, psinfo->bufsize); @@ -448,7 +449,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, record.size = zipped_len; } else { record.size = copy_kmsg_to_buffer(header_size, - dump_size); + dump_size, psinfo, pos); } } else { record.size = header_size + dump_size; @@ -466,6 +467,18 @@ static void pstore_dump(struct kmsg_dumper *dumper, spin_unlock_irqrestore(&psinfo->buf_lock, flags); } +static void pstore_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason) +{ + struct pstore_info_list *entry; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + if (entry->psi->flags & PSTORE_FLAGS_DMESG) + pstore_do_dump(dumper, reason, entry->psi, entry->index); + rcu_read_unlock(); +} + static struct kmsg_dumper pstore_dumper = { .dump = pstore_dump, }; @@ -484,7 +497,8 @@ static void pstore_unregister_kmsg(void) } #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 int c, struct pstore_info *psinfo) { struct pstore_record record; @@ -499,6 +513,17 @@ 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; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + if (entry->psi->flags & PSTORE_FLAGS_CONSOLE) + pstore_console_do_write(con, s, c, entry->psi); + rcu_read_unlock(); +} + static struct console pstore_console = { .write = pstore_console_write, .index = -1, @@ -507,7 +532,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() @@ -558,10 +583,8 @@ static int pstore_write_user_compat(struct pstore_record *record, */ int pstore_register(struct pstore_info *psi) { - if (backend && strcmp(backend, psi->name)) { - pr_warn("ignoring unexpected backend '%s'\n", psi->name); - return -EPERM; - } + struct pstore_info_list *entry; + struct pstore_info_list *pslist; /* Sanity check flags. */ if (!psi->flags) { @@ -577,74 +600,87 @@ int pstore_register(struct pstore_info *psi) return -EINVAL; } - mutex_lock(&psinfo_lock); - if (psinfo) { - pr_warn("backend '%s' already loaded: ignoring '%s'\n", - psinfo->name, psi->name); - mutex_unlock(&psinfo_lock); - return -EBUSY; + mutex_lock(&psback_lock); + + if (psback) { + if (psback->flag == PSTORE_LIST_FULL) { + pr_warn("registration space is used up: ignoring '%s'\n", psi->name); + mutex_unlock(&psback_lock); + return -EBUSY; + } + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) { + if (strcmp(entry->psi->name, psi->name) == 0) { + pr_warn("ignoring unexpected backend '%s'\n", psi->name); + mutex_unlock(&psback_lock); + return -EPERM; + } + } + rcu_read_unlock(); + } + + if (!psback) { + psback = kzalloc(sizeof(*psback), GFP_KERNEL); + 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); - if (psi->flags & PSTORE_FLAGS_DMESG) - allocate_buf_for_compression(); + pslist = kzalloc(sizeof(*pslist), GFP_KERNEL); + pslist->psi = psi; + pslist->index = ffz(psback->flag); + INIT_LIST_HEAD(&pslist->list); + list_add_rcu(&pslist->list, &psback->list_entry); + psback->flag |= (1 << pslist->index); + + mutex_init(&psi->read_mutex); + spin_lock_init(&psi->buf_lock); + + if (psi->flags & PSTORE_FLAGS_DMESG && !psback->front_cnt[PSTORE_TYPE_DMESG]) + allocate_buf_for_compression(psi, pslist->index); - pstore_get_records(0); + pstore_get_records(psi, pslist->index, 0); - if (psi->flags & PSTORE_FLAGS_DMESG) { - pstore_dumper.max_reason = psinfo->max_reason; + if (psi->flags & PSTORE_FLAGS_DMESG && !psback->front_cnt[PSTORE_TYPE_DMESG]++) { + pstore_dumper.max_reason = psi->max_reason; pstore_register_kmsg(); } - 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 - * through /sys/module/pstore/parameters/backend - */ - backend = kstrdup(psi->name, GFP_KERNEL); - 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; /* It's okay to unregister nothing. */ if (!psi) return; - mutex_lock(&psinfo_lock); - - /* Only one backend can be registered at a time. */ - if (WARN_ON(psi != psinfo)) { - mutex_unlock(&psinfo_lock); - return; - } + mutex_lock(&psback_lock); /* 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) + if (psi->flags & PSTORE_FLAGS_DMESG && !--psback->front_cnt[PSTORE_TYPE_DMESG]) pstore_unregister_kmsg(); /* Stop timer and make sure all work has finished. */ @@ -654,16 +690,28 @@ void pstore_unregister(struct pstore_info *psi) /* Remove all backend records from filesystem tree. */ pstore_put_backend_records(psi); - free_buf_for_compression(); + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) { + if (entry->psi == psi) { + free_buf_for_compression(entry->index); + list_del_rcu(&entry->list); + psback->flag ^= 1 << entry->index; + kfree(entry); + break; + } + } + rcu_read_unlock(); - psinfo = NULL; - kfree(backend); - backend = NULL; - mutex_unlock(&psinfo_lock); + if (psback->flag == PSOTRE_LIST_EMPTY) { + kfree(psback); + psback = NULL; + } + + mutex_unlock(&psback_lock); } EXPORT_SYMBOL_GPL(pstore_unregister); -static void decompress_record(struct pstore_record *record) +static void decompress_record(struct pstore_record *record, int pos) { int ret; int unzipped_len; @@ -679,13 +727,13 @@ static void decompress_record(struct pstore_record *record) } /* Missing compression buffer means compression was not initialized. */ - if (!big_oops_buf) { + if (!big_oops_buf[pos]) { pr_warn("no decompression method initialized!\n"); return; } /* Allocate enough space to hold max decompression and ECC. */ - unzipped_len = big_oops_buf_sz; + unzipped_len = big_oops_buf_sz[pos]; workspace = kmalloc(unzipped_len + record->ecc_notice_size, GFP_KERNEL); if (!workspace) @@ -725,7 +773,7 @@ static void decompress_record(struct pstore_record *record) * error records. */ void pstore_get_backend_records(struct pstore_info *psi, - struct dentry *root, int quiet) + struct dentry *root, int quiet, int pos) { int failed = 0; unsigned int stop_loop = 65536; @@ -761,7 +809,7 @@ void pstore_get_backend_records(struct pstore_info *psi, break; } - decompress_record(record); + decompress_record(record, pos); rc = pstore_mkfile(root, record); if (rc) { /* pstore_mkfile() did not take record, so free it. */ @@ -786,7 +834,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; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + pstore_get_records(entry->psi, entry->index, 1); + rcu_read_unlock(); } static void pstore_timefunc(struct timer_list *unused) @@ -817,6 +870,10 @@ static void __init pstore_choose_compression(void) static int __init pstore_init(void) { int ret; + struct pstore_info_list *entry; + + if (!psback) + return 0; pstore_choose_compression(); @@ -825,11 +882,19 @@ static int __init pstore_init(void) * initialize compression because crypto was not ready. If so, * initialize compression now. */ - allocate_buf_for_compression(); + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + allocate_buf_for_compression(entry->psi, entry->index); + rcu_read_unlock(); ret = pstore_init_fs(); - if (ret) - free_buf_for_compression(); + + if (ret) { + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + free_buf_for_compression(entry->index); + rcu_read_unlock(); + } return ret; } diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c index d8542ec2f38c..3cf3dbccd07b 100644 --- a/fs/pstore/pmsg.c +++ b/fs/pstore/pmsg.c @@ -11,8 +11,8 @@ 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 +34,25 @@ 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; + struct pstore_info_list *entry; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) { + if (entry->psi->flags & PSTORE_FLAGS_PMSG) { + int _ret = do_write_pmsg(file, buf, count, ppos, entry->psi); + + ret = ret > _ret ? ret : _ret; + } + } + rcu_read_unlock(); + + return ret; +} + 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 e97a8188f0fd..db085f4b3ca0 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -197,6 +197,33 @@ struct pstore_info { int (*erase)(struct pstore_record *record); }; +/* Supported multibackends */ +#define PSTORE_MAX_BACKEND_LENGTH 100 +#define PSTORE_BACKEND_NUM 16 + +#define PSTORE_LIST_FULL (BIT(PSTORE_BACKEND_NUM) - 1) +#define PSOTRE_LIST_EMPTY 0 + +struct pstore_info_list { + struct pstore_info *psi; + struct list_head list; + int index; +}; + +/** + * struct pstore_backends - management of pstore backends + * @list_entry: entry of pstore backend driver information list + * @front_cnt: count of each enabled frontend + * @flag: bitmap of enabled pstore backend + * + */ + +struct pstore_backends { + struct list_head list_entry; + int front_cnt[PSTORE_TYPE_MAX]; + u16 flag; +}; + /* Supported frontends */ #define PSTORE_FLAGS_DMESG BIT(0) #define PSTORE_FLAGS_CONSOLE BIT(1) -- Gitee From 5890efa66ff9f5f5c6f8c49634998aa210ef0f3a Mon Sep 17 00:00:00 2001 From: Xingrui Yi Date: Wed, 31 Aug 2022 15:16:44 +0800 Subject: [PATCH 2/4] pstore: add subdirs for multibackends With pstore supporting multiple backends, since different backends may enable different kinds of frontends, records in the file system are very confusing. By simply modifying the file system, subdirs are added for each backend, each dir has the same name as the corresponding backend. Signed-off-by: Xingrui Yi --- fs/pstore/inode.c | 71 ++++++++++++++++++++++++++++++++++++------ fs/pstore/internal.h | 1 + fs/pstore/platform.c | 5 ++- include/linux/pstore.h | 2 ++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index cdc44aef9f7f..cf72e8bdf837 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -287,9 +287,14 @@ static const struct super_operations pstore_ops = { .show_options = pstore_show_options, }; -static struct dentry *psinfo_lock_root(struct pstore_info *psinfo) +static struct dentry *psinfo_lock(struct pstore_info *psinfo) { - struct dentry *root; + struct dentry *dentry; + struct qstr qname; + + qname.name = psinfo->name; + qname.len = strlen(psinfo->name); + qname.hash = full_name_hash(pstore_sb->s_root, qname.name, qname.len); mutex_lock(&pstore_sb_lock); /* @@ -301,11 +306,16 @@ static struct dentry *psinfo_lock_root(struct pstore_info *psinfo) return NULL; } - root = pstore_sb->s_root; - inode_lock(d_inode(root)); + dentry = d_lookup(pstore_sb->s_root, &qname); + if (!dentry) { + mutex_unlock(&pstore_sb_lock); + return NULL; + } + + inode_lock(d_inode(dentry)); mutex_unlock(&pstore_sb_lock); - return root; + return dentry; } int pstore_put_backend_records(struct pstore_info *psi) @@ -314,7 +324,7 @@ int pstore_put_backend_records(struct pstore_info *psi) struct dentry *root; int rc = 0; - root = psinfo_lock_root(psi); + root = psinfo_lock(psi); if (!root) return 0; @@ -415,14 +425,51 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record) */ void pstore_get_records(struct pstore_info *psi, int pos, int quiet) { + struct dentry *dentry; + + dentry = psinfo_lock(psi); + if (!dentry) + return; + + pstore_get_backend_records(psi, dentry, quiet, pos); + inode_unlock(d_inode(dentry)); +} + +void pstore_mksubdir(struct pstore_info *psi) +{ + struct dentry *dentry; + struct inode *inode; struct dentry *root; - root = psinfo_lock_root(psi); - if (!root) + if (!psi || !pstore_sb) return; - pstore_get_backend_records(psi, root, quiet, pos); + root = pstore_sb->s_root; + + inode_lock(d_inode(root)); + + dentry = d_alloc_name(pstore_sb->s_root, psi->name); + if (!dentry) + goto fail; + + inode = pstore_get_inode(pstore_sb); + if (!inode) { + dput(dentry); + dentry = ERR_PTR(-ENOMEM); + goto fail; + } + inode->i_mode = S_IFDIR | 0750; + inode->i_op = &pstore_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + inc_nlink(inode); + inc_nlink(d_inode(root)); + + d_add(dentry, inode); + +fail: inode_unlock(d_inode(root)); + return; } static int pstore_fill_super(struct super_block *sb, void *data, int silent) @@ -455,10 +502,14 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent) mutex_unlock(&pstore_sb_lock); rcu_read_lock(); - list_for_each_entry_rcu(entry, &psback->list_entry, list) + list_for_each_entry_rcu(entry, &psback->list_entry, list) { + pstore_mksubdir(entry->psi); pstore_get_records(entry->psi, entry->index, 0); + } rcu_read_unlock(); + psback->fs_ready = true; + return 0; } diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index fac8e331036f..a997e22369a3 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -37,6 +37,7 @@ static inline void pstore_unregister_pmsg(void) {} extern struct pstore_backends *psback; extern void pstore_set_kmsg_bytes(int); +extern void pstore_mksubdir(struct pstore_info *psi); extern void pstore_get_records(struct pstore_info *psi, int pos, int quiet); extern void pstore_get_backend_records(struct pstore_info *psi, diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 24e56c935b5c..a911803a83f6 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -641,7 +641,10 @@ int pstore_register(struct pstore_info *psi) if (psi->flags & PSTORE_FLAGS_DMESG && !psback->front_cnt[PSTORE_TYPE_DMESG]) allocate_buf_for_compression(psi, pslist->index); - pstore_get_records(psi, pslist->index, 0); + if (psback->fs_ready) { + pstore_mksubdir(psi); + pstore_get_records(psi, pslist->index, 0); + } if (psi->flags & PSTORE_FLAGS_DMESG && !psback->front_cnt[PSTORE_TYPE_DMESG]++) { pstore_dumper.max_reason = psi->max_reason; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index db085f4b3ca0..db0eb8e44a41 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -215,6 +215,7 @@ struct pstore_info_list { * @list_entry: entry of pstore backend driver information list * @front_cnt: count of each enabled frontend * @flag: bitmap of enabled pstore backend + * @fs_ready: whether the pstore filesystem is ready * */ @@ -222,6 +223,7 @@ struct pstore_backends { struct list_head list_entry; int front_cnt[PSTORE_TYPE_MAX]; u16 flag; + bool fs_ready; }; /* Supported frontends */ -- Gitee From cc1ee0601c8f48961a8b1f09ff2f7b9618d387bb Mon Sep 17 00:00:00 2001 From: Xingrui Yi Date: Wed, 31 Aug 2022 20:00:38 +0800 Subject: [PATCH 3/4] pstore: remove the module parameter "backend" Since all enabled backends have corresponding subdir in /sys/fs/pstore, and the logic of finding unexpected is changed by using psinfo_list rather than parameter backend, it is not necessary to retain the parameter backend. Remove the unnecessary module parameter "backend". Signed-off-by: Xingrui Yi --- fs/pstore/platform.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index a911803a83f6..941ae8187989 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -76,10 +76,6 @@ static DECLARE_WORK(pstore_work, pstore_dowork); static DEFINE_MUTEX(psback_lock); struct pstore_backends *psback; -static char *backend; -module_param(backend, charp, 0444); -MODULE_PARM_DESC(backend, "specific backend to use"); - static char *compress = #ifdef CONFIG_PSTORE_COMPRESS_DEFAULT CONFIG_PSTORE_COMPRESS_DEFAULT; -- Gitee From 25d36c67b4a4668eee4bdf9e3f71ac054a6552f2 Mon Sep 17 00:00:00 2001 From: Xingrui Yi Date: Fri, 2 Sep 2022 13:02:18 +0800 Subject: [PATCH 4/4] pstore: add ttyprobe frontend Provide a pstore frontend which can log all messages that are send to tty drivers when there are some problems with drivers or there is no access to serial ports. Using pmsg requires us to redirect the output of the user state. When we need to globally view the serial output of various processes, it is tedious to redirect the output of each process. We think pmsg is more suitable for targeted viewing of certain processes' output, and we also need a tool that can quickly do a global view so that we can get user-state printed data if the tty driver is working abnormally or if we don't have serial access. Signed-off-by: Xingrui Yi --- fs/pstore/Kconfig | 23 +++++++ fs/pstore/Makefile | 2 + fs/pstore/blk.c | 10 +++ fs/pstore/internal.h | 10 +++ fs/pstore/platform.c | 8 +++ fs/pstore/ram.c | 40 ++++++++++- fs/pstore/ttyprobe.c | 134 ++++++++++++++++++++++++++++++++++++ fs/pstore/zone.c | 42 ++++++++++- include/linux/pstore.h | 2 + include/linux/pstore_blk.h | 2 + include/linux/pstore_ram.h | 1 + include/linux/pstore_zone.h | 2 + 12 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 fs/pstore/ttyprobe.c diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 8efe60487b48..f996c853e142 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -138,6 +138,17 @@ config PSTORE_FTRACE If unsure, say N. +config PSTORE_TTYPROBE + bool "Log tty messages" + depends on PSTORE + help + When the option is enabled, pstore will log all messages send + to tty drivers, even if no oops or panic happened. It can be + useful to log serial information when there is no access to + serial ports or the tty drivers work abnormally. + + If unsure, say N. + config PSTORE_RAM tristate "Log panic/oops to a RAM buffer" depends on PSTORE @@ -263,3 +274,15 @@ config PSTORE_BLK_FTRACE_SIZE NOTE that, both Kconfig and module parameters can configure pstore/blk, but module parameters have priority over Kconfig. + +config PSTORE_BLK_TTYPROBE_SIZE + int "Size in Kbytes of ttyprobe log to store" + depends on PSTORE_BLK + depends on PSTORE_TTYPROBE + default 64 + help + This just sets size of ttyprobe log (ttyprobe_size) for pstore/blk. The + size is in KB and must be a multiple of 4. + + NOTE that, both Kconfig and module parameters can configure + pstore/blk, but module parameters have priority over Kconfig. diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile index c270467aeece..3bd2e9129f6e 100644 --- a/fs/pstore/Makefile +++ b/fs/pstore/Makefile @@ -10,6 +10,8 @@ pstore-$(CONFIG_PSTORE_FTRACE) += ftrace.o pstore-$(CONFIG_PSTORE_PMSG) += pmsg.o +pstore-$(CONFIG_PSTORE_TTYPROBE) += ttyprobe.o + ramoops-objs += ram.o ram_core.o obj-$(CONFIG_PSTORE_RAM) += ramoops.o diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c index fcd5563dde06..4dda0472988c 100644 --- a/fs/pstore/blk.c +++ b/fs/pstore/blk.c @@ -51,6 +51,14 @@ static long ftrace_size = -1; module_param(ftrace_size, long, 0400); MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes"); +#if IS_ENABLED(CONFIG_PSTORE_TTYPROBE) +static long ttyprobe_size = CONFIG_PSTORE_BLK_TTYPROBE_SIZE; +#else +static long ttyprobe_size = -1; +#endif +module_param(ttyprobe_size, long, 0400); +MODULE_PARM_DESC(ttyprobe_size, "ttyprobe_size size in kbytes"); + static bool best_effort; module_param(best_effort, bool, 0400); MODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)"); @@ -144,6 +152,7 @@ static int __register_pstore_device(struct pstore_device_info *dev) verify_size(pmsg_size, 4096, dev->flags & PSTORE_FLAGS_PMSG); verify_size(console_size, 4096, dev->flags & PSTORE_FLAGS_CONSOLE); verify_size(ftrace_size, 4096, dev->flags & PSTORE_FLAGS_FTRACE); + verify_size(ttyprobe_size, 4096, dev->flags & PSTORE_FLAGS_TTYPROBE); #undef verify_size pstore_zone_info->total_size = dev->total_size; @@ -476,6 +485,7 @@ int pstore_blk_get_config(struct pstore_blk_config *info) info->pmsg_size = check_size(pmsg_size, 4096); info->ftrace_size = check_size(ftrace_size, 4096); info->console_size = check_size(console_size, 4096); + info->ttyprobe_size = check_size(ttyprobe_size, 4096); return 0; } diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index a997e22369a3..bfae676ec087 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -34,6 +34,16 @@ static inline void pstore_register_pmsg(void) {} static inline void pstore_unregister_pmsg(void) {} #endif +#ifdef CONFIG_PSTORE_TTYPROBE +extern void pstore_register_ttyprobe(void); +extern void pstore_start_ttyprobe(void); +extern void pstore_unregister_ttyprobe(void); +#else +static inline void pstore_register_ttyprobe(void) {} +static inline void pstore_start_ttyprobe(void) {} +static inline void pstore_unregister_ttyprobe(void) {} +#endif + extern struct pstore_backends *psback; extern void pstore_set_kmsg_bytes(int); diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 941ae8187989..318533519779 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -58,6 +58,7 @@ static const char * const pstore_type_names[] = { "powerpc-common", "pmsg", "powerpc-opal", + "ttyprobe", }; static int pstore_new_entry; @@ -652,6 +653,8 @@ int pstore_register(struct pstore_info *psi) pstore_register_ftrace(); if (psi->flags & PSTORE_FLAGS_PMSG && !psback->front_cnt[PSTORE_TYPE_PMSG]++) pstore_register_pmsg(); + if (psi->flags & PSTORE_FLAGS_TTYPROBE && !psback->front_cnt[PSTORE_TYPE_TTYPROBE]++) + pstore_register_ttyprobe(); /* Start watching for new records, if desired. */ pstore_timer_kick(); @@ -681,6 +684,8 @@ void pstore_unregister(struct pstore_info *psi) pstore_unregister_console(); if (psi->flags & PSTORE_FLAGS_DMESG && !--psback->front_cnt[PSTORE_TYPE_DMESG]) pstore_unregister_kmsg(); + if (psi->flags & PSTORE_FLAGS_TTYPROBE && !--psback->front_cnt[PSTORE_TYPE_TTYPROBE]) + pstore_unregister_ttyprobe(); /* Stop timer and make sure all work has finished. */ del_timer_sync(&pstore_timer); @@ -874,6 +879,9 @@ static int __init pstore_init(void) if (!psback) return 0; + /* Probe of tty drivers must start after all tty drivers init */ + pstore_start_ttyprobe(); + pstore_choose_compression(); /* diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index ca6d8a867285..4ff4f5228e29 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -43,6 +43,10 @@ static ulong ramoops_pmsg_size = MIN_MEM_SIZE; module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400); MODULE_PARM_DESC(pmsg_size, "size of user space message log"); +static ulong ramoops_ttyprobe_size = MIN_MEM_SIZE; +module_param_named(ttyprobe_size, ramoops_ttyprobe_size, ulong, 0400); +MODULE_PARM_DESC(ttyprobe_size, "size of ttyprobe message log"); + static unsigned long long mem_address; module_param_hw(mem_address, ullong, other, 0400); MODULE_PARM_DESC(mem_address, @@ -80,6 +84,7 @@ struct ramoops_context { 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 *tprz; /* TTYPROBE zone */ phys_addr_t phys_addr; unsigned long size; unsigned int memtype; @@ -87,6 +92,7 @@ struct ramoops_context { size_t console_size; size_t ftrace_size; size_t pmsg_size; + size_t ttyprobe_size; u32 flags; struct persistent_ram_ecc_info ecc_info; unsigned int max_dump_cnt; @@ -97,6 +103,7 @@ struct ramoops_context { unsigned int max_ftrace_cnt; unsigned int ftrace_read_cnt; unsigned int pmsg_read_cnt; + unsigned int ttyprobe_read_cnt; struct pstore_info pstore; }; @@ -110,6 +117,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) cxt->console_read_cnt = 0; cxt->ftrace_read_cnt = 0; cxt->pmsg_read_cnt = 0; + cxt->ttyprobe_read_cnt = 0; return 0; } @@ -213,6 +221,9 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record) if (!prz_ok(prz) && !cxt->pmsg_read_cnt++) prz = ramoops_get_next_prz(&cxt->mprz, 0 /* single */, record); + if (!prz_ok(prz) && !cxt->ttyprobe_read_cnt++) + prz = ramoops_get_next_prz(&cxt->tprz, 0 /* single */, record); + /* ftrace is last since it may want to dynamically allocate memory. */ if (!prz_ok(prz)) { if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU) && @@ -334,6 +345,11 @@ static int notrace ramoops_pstore_write(struct pstore_record *record) } 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_TTYPROBE) { + if (!cxt->tprz) + return -ENOMEM; + persistent_ram_write(cxt->tprz, record->buf, record->size); + return 0; } if (record->type != PSTORE_TYPE_DMESG) @@ -425,6 +441,9 @@ static int ramoops_pstore_erase(struct pstore_record *record) case PSTORE_TYPE_PMSG: prz = cxt->mprz; break; + case PSTORE_TYPE_TTYPROBE: + prz = cxt->tprz; + break; default: return -EINVAL; } @@ -670,6 +689,7 @@ static int ramoops_parse_dt(struct platform_device *pdev, parse_u32("console-size", pdata->console_size, 0); parse_u32("ftrace-size", pdata->ftrace_size, 0); parse_u32("pmsg-size", pdata->pmsg_size, 0); + parse_u32("ttyprobe-size", pdata->ttyprobe_size, 0); parse_u32("ecc-size", pdata->ecc_info.ecc_size, 0); parse_u32("flags", pdata->flags, 0); parse_u32("max-reason", pdata->max_reason, pdata->max_reason); @@ -690,9 +710,10 @@ static int ramoops_parse_dt(struct platform_device *pdev, parent_node = of_get_parent(of_node); if (!of_node_name_eq(parent_node, "reserved-memory") && !pdata->console_size && !pdata->ftrace_size && - !pdata->pmsg_size && !pdata->ecc_info.ecc_size) { + !pdata->pmsg_size && !pdata->ttyprobe_size && !pdata->ecc_info.ecc_size) { pdata->console_size = pdata->record_size; pdata->pmsg_size = pdata->record_size; + pdata->ttyprobe_size = pdata->record_size; } of_node_put(parent_node); @@ -748,6 +769,8 @@ static int ramoops_probe(struct platform_device *pdev) 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->ttyprobe_size && !is_power_of_2(pdata->ttyprobe_size)) + pdata->ttyprobe_size = rounddown_pow_of_two(pdata->ttyprobe_size); cxt->size = pdata->mem_size; cxt->phys_addr = pdata->mem_address; @@ -756,13 +779,14 @@ static int ramoops_probe(struct platform_device *pdev) cxt->console_size = pdata->console_size; cxt->ftrace_size = pdata->ftrace_size; cxt->pmsg_size = pdata->pmsg_size; + cxt->ttyprobe_size = pdata->ttyprobe_size; 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->ttyprobe_size; err = ramoops_init_przs("dmesg", dev, cxt, &cxt->dprzs, &paddr, dump_mem_sz, cxt->record_size, &cxt->max_dump_cnt, 0, 0); @@ -790,6 +814,11 @@ static int ramoops_probe(struct platform_device *pdev) if (err) goto fail_init_mprz; + err = ramoops_init_prz("ttyprobe", dev, cxt, &cxt->tprz, &paddr, + cxt->ttyprobe_size, 0); + if (err) + goto fail_init_tprz; + cxt->pstore.data = cxt; /* * Prepare frontend flags based on which areas are initialized. @@ -808,6 +837,8 @@ static int ramoops_probe(struct platform_device *pdev) cxt->pstore.flags |= PSTORE_FLAGS_FTRACE; if (cxt->pmsg_size) cxt->pstore.flags |= PSTORE_FLAGS_PMSG; + if (cxt->ttyprobe_size) + cxt->pstore.flags |= PSTORE_FLAGS_TTYPROBE; /* * Since bufsize is only used for dmesg crash dumps, it @@ -841,6 +872,7 @@ static int ramoops_probe(struct platform_device *pdev) ramoops_console_size = pdata->console_size; ramoops_pmsg_size = pdata->pmsg_size; ramoops_ftrace_size = pdata->ftrace_size; + ramoops_ttyprobe_size = pdata->ttyprobe_size; pr_info("using 0x%lx@0x%llx, ecc: %d\n", cxt->size, (unsigned long long)cxt->phys_addr, @@ -852,6 +884,8 @@ static int ramoops_probe(struct platform_device *pdev) kfree(cxt->pstore.buf); fail_clear: cxt->pstore.bufsize = 0; + persistent_ram_free(cxt->tprz); +fail_init_tprz: persistent_ram_free(cxt->mprz); fail_init_mprz: fail_init_fprz: @@ -872,6 +906,7 @@ static int ramoops_remove(struct platform_device *pdev) cxt->pstore.bufsize = 0; persistent_ram_free(cxt->mprz); + persistent_ram_free(cxt->tprz); persistent_ram_free(cxt->cprz); ramoops_free_przs(cxt); @@ -920,6 +955,7 @@ static void __init ramoops_register_dummy(void) pdata.console_size = ramoops_console_size; pdata.ftrace_size = ramoops_ftrace_size; pdata.pmsg_size = ramoops_pmsg_size; + pdata.ttyprobe_size = ramoops_ttyprobe_size; /* If "max_reason" is set, its value has priority over "dump_oops". */ if (ramoops_max_reason >= 0) pdata.max_reason = ramoops_max_reason; diff --git a/fs/pstore/ttyprobe.c b/fs/pstore/ttyprobe.c new file mode 100644 index 000000000000..83e5102ea2df --- /dev/null +++ b/fs/pstore/ttyprobe.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Provide a pstore frontend which can log all messages that are send + * to tty drivers when there are some problems with drivers or there + * is no access to serial ports. + */ + +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static DEFINE_MUTEX(ttymsg_lock); + +#define TTYPROBE_NAME "ttyprobe" +#undef pr_fmt +#define pr_fmt(fmt) TTYPROBE_NAME ": " fmt +#define PSTORE_TTYPROBE_REGISTERED 1 +#define PSTORE_TTYPROBE_UNREGISTERED 0 + +struct list_kprobe { + struct kprobe *kp; + struct list_head list; +}; +static LIST_HEAD(kprobe_head); + +bool pstore_ttyprobe_status = PSTORE_TTYPROBE_UNREGISTERED; + +static void do_write_ttymsg(struct kprobe *p, struct pt_regs *regs, + unsigned long flags, struct pstore_info *psinfo) +{ + struct pstore_record record; + const unsigned char *buf; + int count; + +#ifdef CONFIG_ARM64 + buf = (const unsigned char *)regs->regs[1]; + count = (int)regs->regs[2]; +#endif + +#ifdef CONFIG_X86 + buf = (const unsigned char *)regs->si; + count = (int)regs->dx; +#endif + + if (!count || !buf) + return; + + pstore_record_init(&record, psinfo); + record.type = PSTORE_TYPE_TTYPROBE; + record.size = count; + record.buf = (char *)buf; + mutex_lock(&ttymsg_lock); + psinfo->write(&record); + mutex_unlock(&ttymsg_lock); +} + +static void write_ttymsg(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + struct pstore_info_list *entry; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &psback->list_entry, list) + if (entry->psi->flags & PSTORE_FLAGS_TTYPROBE) + do_write_ttymsg(p, regs, flags, entry->psi); + rcu_read_unlock(); +} + +void pstore_register_ttyprobe(void) +{ + pstore_ttyprobe_status = PSTORE_TTYPROBE_REGISTERED; +} + +void pstore_start_ttyprobe(void) +{ + struct console *cs[16]; + struct console *c; + struct tty_driver *drv; + int i = 0, ret; + + if (pstore_ttyprobe_status == PSTORE_TTYPROBE_UNREGISTERED) + return; + + console_lock(); + for_each_console(c) { + if (!c->device) + continue; + if ((c->flags & CON_ENABLED) == 0) + continue; + cs[i++] = c; + if (i >= ARRAY_SIZE(cs)) + break; + } + while (i--) { + static struct list_kprobe *kpl; + int index = cs[i]->index; + + drv = cs[i]->device(cs[i], &index); + if (drv != NULL) { + kpl = kzalloc(sizeof(*kpl), GFP_KERNEL); + kpl->kp = kzalloc(sizeof(*kpl->kp), GFP_KERNEL); + kpl->kp->post_handler = write_ttymsg; + kpl->kp->addr = (kprobe_opcode_t *)drv->ops->write; + INIT_LIST_HEAD(&kpl->list); + list_add(&kpl->list, &kprobe_head); + + ret = register_kprobe(kpl->kp); + if (ret < 0) { + pr_err("register_kprobe failed, returned %d\n", + ret); + continue; + } + pr_info("planted kprobe at %p\n", kpl->kp->addr); + } else + pr_err("tty driver is NULL, plant kprobe failed"); + } + console_unlock(); +} + +void pstore_unregister_ttyprobe(void) +{ + struct list_kprobe *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &kprobe_head, list) { + unregister_kprobe(entry->kp); + pr_info("kprobe at %p unregistered\n", entry->kp->addr); + list_del(&entry->list); + kfree(entry); + } +} diff --git a/fs/pstore/zone.c b/fs/pstore/zone.c index 3ce89216670c..cd90a3db5ee5 100644 --- a/fs/pstore/zone.c +++ b/fs/pstore/zone.c @@ -93,6 +93,7 @@ struct pstore_zone { * @ppsz: pmsg storage zone * @cpsz: console storage zone * @fpszs: ftrace storage zones + * @tpsz: ttyprobe storage zone * @kmsg_max_cnt: max count of @kpszs * @kmsg_read_cnt: counter of total read kmsg dumps * @kmsg_write_cnt: counter of total kmsg dump writes @@ -100,6 +101,7 @@ struct pstore_zone { * @console_read_cnt: counter of total read console zone * @ftrace_max_cnt: max count of @fpszs * @ftrace_read_cnt: counter of max read ftrace zone + * @ttyprobe_read_cnt: counter of total read ttyprobe zone * @oops_counter: counter of oops dumps * @panic_counter: counter of panic dumps * @recovered: whether finished recovering data from storage @@ -113,6 +115,7 @@ struct psz_context { struct pstore_zone *ppsz; struct pstore_zone *cpsz; struct pstore_zone **fpszs; + struct pstore_zone *tpsz; unsigned int kmsg_max_cnt; unsigned int kmsg_read_cnt; unsigned int kmsg_write_cnt; @@ -120,6 +123,7 @@ struct psz_context { unsigned int console_read_cnt; unsigned int ftrace_max_cnt; unsigned int ftrace_read_cnt; + unsigned int ttyprobe_read_cnt; /* * These counters should be calculated during recovery. * It records the oops/panic times after crashes rather than boots. @@ -325,6 +329,8 @@ static void psz_flush_all_dirty_zones(struct work_struct *work) ret |= psz_flush_dirty_zones(cxt->kpszs, cxt->kmsg_max_cnt); if (cxt->fpszs) ret |= psz_flush_dirty_zones(cxt->fpszs, cxt->ftrace_max_cnt); + if (cxt->tpsz) + ret |= psz_flush_dirty_zone(cxt->tpsz); if (ret && cxt->pstore_zone_info) schedule_delayed_work(&psz_cleaner, msecs_to_jiffies(1000)); } @@ -617,6 +623,10 @@ static inline int psz_recovery(struct psz_context *cxt) if (ret) goto out; + ret = psz_recover_zone(cxt, cxt->tpsz); + if (ret) + goto out; + ret = psz_recover_zones(cxt, cxt->fpszs, cxt->ftrace_max_cnt); out: @@ -637,6 +647,7 @@ static int psz_pstore_open(struct pstore_info *psi) cxt->pmsg_read_cnt = 0; cxt->console_read_cnt = 0; cxt->ftrace_read_cnt = 0; + cxt->ttyprobe_read_cnt = 0; return 0; } @@ -713,6 +724,8 @@ static int psz_pstore_erase(struct pstore_record *record) if (record->id >= cxt->ftrace_max_cnt) return -EINVAL; return psz_record_erase(cxt, cxt->fpszs[record->id]); + case PSTORE_TYPE_TTYPROBE: + return psz_record_erase(cxt, cxt->tpsz); default: return -EINVAL; } } @@ -891,6 +904,8 @@ static int notrace psz_pstore_write(struct pstore_record *record) return psz_record_write(cxt->cpsz, record); case PSTORE_TYPE_PMSG: return psz_record_write(cxt->ppsz, record); + case PSTORE_TYPE_TTYPROBE: + return psz_record_write(cxt->tpsz, record); case PSTORE_TYPE_FTRACE: { int zonenum = smp_processor_id(); @@ -935,6 +950,13 @@ static struct pstore_zone *psz_read_next_zone(struct psz_context *cxt) return zone; } + if (cxt->ttyprobe_read_cnt == 0) { + cxt->ttyprobe_read_cnt++; + zone = cxt->tpsz; + if (psz_old_ok(zone)) + return zone; + } + return NULL; } @@ -1081,6 +1103,7 @@ static ssize_t psz_pstore_read(struct pstore_record *record) readop = psz_ftrace_read; break; case PSTORE_TYPE_CONSOLE: + case PSTORE_TYPE_TTYPROBE: case PSTORE_TYPE_PMSG: readop = psz_record_read; break; @@ -1145,6 +1168,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->tpsz) + psz_free_zone(&cxt->tpsz); } static struct pstore_zone *psz_init_zone(enum pstore_type_id type, @@ -1255,6 +1280,15 @@ static int psz_alloc_zones(struct psz_context *cxt) goto free_out; } + off_size += info->ttyprobe_size; + cxt->tpsz = psz_init_zone(PSTORE_TYPE_TTYPROBE, &off, + info->ttyprobe_size); + if (IS_ERR(cxt->tpsz)) { + err = PTR_ERR(cxt->tpsz); + cxt->tpsz = NULL; + goto free_out; + } + off_size += info->ftrace_size; cxt->fpszs = psz_init_zones(PSTORE_TYPE_FTRACE, &off, info->ftrace_size, @@ -1301,7 +1335,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->ttyprobe_size) { pr_warn("at least one record size must be non-zero\n"); return -EINVAL; } @@ -1326,6 +1360,7 @@ int register_pstore_zone(struct pstore_zone_info *info) check_size(pmsg_size, SECTOR_SIZE); check_size(console_size, SECTOR_SIZE); check_size(ftrace_size, SECTOR_SIZE); + check_size(ttyprobe_size, SECTOR_SIZE); #undef check_size @@ -1354,6 +1389,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("\tttyprobe size : %ld Bytes\n", info->ttyprobe_size); err = psz_alloc_zones(cxt); if (err) { @@ -1395,6 +1431,10 @@ int register_pstore_zone(struct pstore_zone_info *info) cxt->pstore.flags |= PSTORE_FLAGS_FTRACE; pr_cont(" ftrace"); } + if (info->ttyprobe_size) { + cxt->pstore.flags |= PSTORE_FLAGS_TTYPROBE; + pr_cont(" ttyprobe"); + } pr_cont("\n"); err = pstore_register(&cxt->pstore); diff --git a/include/linux/pstore.h b/include/linux/pstore.h index db0eb8e44a41..96c5cb7bbb06 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -38,6 +38,7 @@ enum pstore_type_id { PSTORE_TYPE_PPC_COMMON = 6, PSTORE_TYPE_PMSG = 7, PSTORE_TYPE_PPC_OPAL = 8, + PSTORE_TYPE_TTYPROBE = 9, /* End of the list */ PSTORE_TYPE_MAX @@ -231,6 +232,7 @@ struct pstore_backends { #define PSTORE_FLAGS_CONSOLE BIT(1) #define PSTORE_FLAGS_FTRACE BIT(2) #define PSTORE_FLAGS_PMSG BIT(3) +#define PSTORE_FLAGS_TTYPROBE BIT(4) extern int pstore_register(struct pstore_info *); extern void pstore_unregister(struct pstore_info *); diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h index 61e914522b01..1267968186e4 100644 --- a/include/linux/pstore_blk.h +++ b/include/linux/pstore_blk.h @@ -96,6 +96,7 @@ void unregister_pstore_device(struct pstore_device_info *dev); * @pmsg_size: Total size of the pmsg storage area * @console_size: Total size of the console storage area * @ftrace_size: Total size for ftrace logging data (for all CPUs) + * @ttyprobe_size: Total size of the ttyprobe storage area */ struct pstore_blk_config { char device[80]; @@ -104,6 +105,7 @@ struct pstore_blk_config { unsigned long pmsg_size; unsigned long console_size; unsigned long ftrace_size; + unsigned long ttyprobe_size; }; /** diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 9f16afec7290..69427ab6c7c7 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -133,6 +133,7 @@ struct ramoops_platform_data { unsigned long console_size; unsigned long ftrace_size; unsigned long pmsg_size; + unsigned long ttyprobe_size; int max_reason; u32 flags; struct persistent_ram_ecc_info ecc_info; diff --git a/include/linux/pstore_zone.h b/include/linux/pstore_zone.h index 1e35eaa33e5e..64b409f24e8b 100644 --- a/include/linux/pstore_zone.h +++ b/include/linux/pstore_zone.h @@ -21,6 +21,7 @@ typedef ssize_t (*pstore_zone_erase_op)(size_t, loff_t); * @pmsg_size: The size of pmsg zone which is the same as @kmsg_size. * @console_size:The size of console zone which is the same as @kmsg_size. * @ftrace_size:The size of ftrace zone which is the same as @kmsg_size. + * @ttyprobe_size:The size of ttyprobe zone which is the same as @kmsg_size. * @read: The general read operation. Both of the function parameters * @size and @offset are relative value to storage. * On success, the number of bytes should be returned, others @@ -48,6 +49,7 @@ struct pstore_zone_info { unsigned long pmsg_size; unsigned long console_size; unsigned long ftrace_size; + unsigned long ttyprobe_size; pstore_zone_read_op read; pstore_zone_write_op write; pstore_zone_erase_op erase; -- Gitee