From: Kent Overstreet <kent.overstreet@xxxxxxxxx> This converts dynamic debug to the new code tagging framework, which provides an interface for iterating over objects in a particular elf section. It also converts the debugfs interface from seq_file to the style used by other code tagging users, which also makes the code a bit smaller and simpler. It doesn't yet convert struct _ddebug to use struct codetag; another cleanup could convert it to that, and to codetag_query_parse(). Signed-off-by: Kent Overstreet <kent.overstreet@xxxxxxxxx> Cc: Jason Baron <jbaron@xxxxxxxxxx> Cc: Luis Chamberlain <mcgrof@xxxxxxxxxx> --- include/asm-generic/codetag.lds.h | 5 +- include/asm-generic/vmlinux.lds.h | 5 - include/linux/dynamic_debug.h | 11 +- kernel/module/internal.h | 2 - kernel/module/main.c | 23 -- lib/dynamic_debug.c | 452 ++++++++++-------------------- 6 files changed, 158 insertions(+), 340 deletions(-) diff --git a/include/asm-generic/codetag.lds.h b/include/asm-generic/codetag.lds.h index b087cf1874a9..b7e351f80e9e 100644 --- a/include/asm-generic/codetag.lds.h +++ b/include/asm-generic/codetag.lds.h @@ -8,10 +8,11 @@ KEEP(*(_name)) \ __stop_##_name = .; -#define CODETAG_SECTIONS() \ +#define CODETAG_SECTIONS() \ SECTION_WITH_BOUNDARIES(alloc_tags) \ SECTION_WITH_BOUNDARIES(dynamic_fault_tags) \ SECTION_WITH_BOUNDARIES(time_stats_tags) \ - SECTION_WITH_BOUNDARIES(error_code_tags) + SECTION_WITH_BOUNDARIES(error_code_tags) \ + SECTION_WITH_BOUNDARIES(dyndbg) #endif /* __ASM_GENERIC_CODETAG_LDS_H */ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index c2dc2a59ab2e..d3fb914d157f 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -345,11 +345,6 @@ __end_once = .; \ STRUCT_ALIGN(); \ *(__tracepoints) \ - /* implement dynamic printk debug */ \ - . = ALIGN(8); \ - __start___dyndbg = .; \ - KEEP(*(__dyndbg)) \ - __stop___dyndbg = .; \ CODETAG_SECTIONS() \ LIKELY_PROFILE() \ BRANCH_PROFILE() \ diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index dce631e678dd..6a57009dd29e 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -58,9 +58,6 @@ struct _ddebug { /* exported for module authors to exercise >control */ int dynamic_debug_exec_queries(const char *query, const char *modname); -int ddebug_add_module(struct _ddebug *tab, unsigned int n, - const char *modname); -extern int ddebug_remove_module(const char *mod_name); extern __printf(2, 3) void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...); @@ -89,7 +86,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor, #define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) \ static struct _ddebug __aligned(8) \ - __section("__dyndbg") name = { \ + __section("dyndbg") name = { \ .modname = KBUILD_MODNAME, \ .function = __func__, \ .filename = __FILE__, \ @@ -187,12 +184,6 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor, #include <linux/errno.h> #include <linux/printk.h> -static inline int ddebug_add_module(struct _ddebug *tab, unsigned int n, - const char *modname) -{ - return 0; -} - static inline int ddebug_remove_module(const char *mod) { return 0; diff --git a/kernel/module/internal.h b/kernel/module/internal.h index f1b6c477bd93..f867c57ab74f 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -62,8 +62,6 @@ struct load_info { Elf_Shdr *sechdrs; char *secstrings, *strtab; unsigned long symoffs, stroffs, init_typeoffs, core_typeoffs; - struct _ddebug *debug; - unsigned int num_debug; bool sig_ok; #ifdef CONFIG_KALLSYMS unsigned long mod_kallsyms_init_off; diff --git a/kernel/module/main.c b/kernel/module/main.c index d253277492fd..28e3b337841b 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1163,9 +1163,6 @@ static void free_module(struct module *mod) mod->state = MODULE_STATE_UNFORMED; mutex_unlock(&module_mutex); - /* Remove dynamic debug info */ - ddebug_remove_module(mod->name); - /* Arch-specific cleanup. */ module_arch_cleanup(mod); @@ -1600,19 +1597,6 @@ static void free_modinfo(struct module *mod) } } -static void dynamic_debug_setup(struct module *mod, struct _ddebug *debug, unsigned int num) -{ - if (!debug) - return; - ddebug_add_module(debug, num, mod->name); -} - -static void dynamic_debug_remove(struct module *mod, struct _ddebug *debug) -{ - if (debug) - ddebug_remove_module(mod->name); -} - void * __weak module_alloc(unsigned long size) { return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END, @@ -2113,9 +2097,6 @@ static int find_module_sections(struct module *mod, struct load_info *info) if (section_addr(info, "__obsparm")) pr_warn("%s: Ignoring obsolete parameters\n", mod->name); - info->debug = section_objs(info, "__dyndbg", - sizeof(*info->debug), &info->num_debug); - return 0; } @@ -2808,9 +2789,6 @@ static int load_module(struct load_info *info, const char __user *uargs, goto free_arch_cleanup; } - init_build_id(mod, info); - dynamic_debug_setup(mod, info->debug, info->num_debug); - /* Ftrace init must be called in the MODULE_STATE_UNFORMED state */ ftrace_module_init(mod); @@ -2875,7 +2853,6 @@ static int load_module(struct load_info *info, const char __user *uargs, ddebug_cleanup: ftrace_release_mod(mod); - dynamic_debug_remove(mod, info->debug); synchronize_rcu(); kfree(mod->args); free_arch_cleanup: diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index dd7f56af9aed..e9079825fb3b 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "dyndbg: " fmt +#include <linux/codetag.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -36,19 +37,37 @@ #include <linux/sched.h> #include <linux/device.h> #include <linux/netdevice.h> +#include <linux/seq_buf.h> #include <rdma/ib_verbs.h> -extern struct _ddebug __start___dyndbg[]; -extern struct _ddebug __stop___dyndbg[]; +static struct codetag_type *cttype; -struct ddebug_table { - struct list_head link; - const char *mod_name; - unsigned int num_ddebugs; - struct _ddebug *ddebugs; +struct user_buf { + char __user *buf; /* destination user buffer */ + size_t size; /* size of requested read */ + ssize_t ret; /* bytes read so far */ }; +static int flush_ubuf(struct user_buf *dst, struct seq_buf *src) +{ + if (src->len) { + size_t bytes = min_t(size_t, src->len, dst->size); + int err = copy_to_user(dst->buf, src->buffer, bytes); + + if (err) + return err; + + dst->ret += bytes; + dst->buf += bytes; + dst->size -= bytes; + src->len -= bytes; + memmove(src->buffer, src->buffer + bytes, src->len); + } + + return 0; +} + struct ddebug_query { const char *filename; const char *module; @@ -58,8 +77,9 @@ struct ddebug_query { }; struct ddebug_iter { - struct ddebug_table *table; - unsigned int idx; + struct codetag_iterator ct_iter; + struct seq_buf buf; + char rawbuf[4096]; }; struct flag_settings { @@ -67,8 +87,6 @@ struct flag_settings { unsigned int mask; }; -static DEFINE_MUTEX(ddebug_lock); -static LIST_HEAD(ddebug_tables); static int verbose; module_param(verbose, int, 0644); MODULE_PARM_DESC(verbose, " dynamic_debug/control processing " @@ -152,78 +170,76 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg) static int ddebug_change(const struct ddebug_query *query, struct flag_settings *modifiers) { - int i; - struct ddebug_table *dt; + struct codetag_iterator ct_iter; + struct codetag *ct; unsigned int newflags; unsigned int nfound = 0; struct flagsbuf fbuf; - /* search for matching ddebugs */ - mutex_lock(&ddebug_lock); - list_for_each_entry(dt, &ddebug_tables, link) { + codetag_lock_module_list(cttype, true); + codetag_init_iter(&ct_iter, cttype); + + while ((ct = codetag_next_ct(&ct_iter))) { + struct _ddebug *dp = (void *) ct; /* match against the module name */ if (query->module && - !match_wildcard(query->module, dt->mod_name)) + !match_wildcard(query->module, dp->modname)) continue; - for (i = 0; i < dt->num_ddebugs; i++) { - struct _ddebug *dp = &dt->ddebugs[i]; - - /* match against the source filename */ - if (query->filename && - !match_wildcard(query->filename, dp->filename) && - !match_wildcard(query->filename, - kbasename(dp->filename)) && - !match_wildcard(query->filename, - trim_prefix(dp->filename))) - continue; + /* match against the source filename */ + if (query->filename && + !match_wildcard(query->filename, dp->filename) && + !match_wildcard(query->filename, + kbasename(dp->filename)) && + !match_wildcard(query->filename, + trim_prefix(dp->filename))) + continue; - /* match against the function */ - if (query->function && - !match_wildcard(query->function, dp->function)) - continue; + /* match against the function */ + if (query->function && + !match_wildcard(query->function, dp->function)) + continue; - /* match against the format */ - if (query->format) { - if (*query->format == '^') { - char *p; - /* anchored search. match must be at beginning */ - p = strstr(dp->format, query->format+1); - if (p != dp->format) - continue; - } else if (!strstr(dp->format, query->format)) + /* match against the format */ + if (query->format) { + if (*query->format == '^') { + char *p; + /* anchored search. match must be at beginning */ + p = strstr(dp->format, query->format+1); + if (p != dp->format) continue; - } - - /* match against the line number range */ - if (query->first_lineno && - dp->lineno < query->first_lineno) - continue; - if (query->last_lineno && - dp->lineno > query->last_lineno) + } else if (!strstr(dp->format, query->format)) continue; + } + + /* match against the line number range */ + if (query->first_lineno && + dp->lineno < query->first_lineno) + continue; + if (query->last_lineno && + dp->lineno > query->last_lineno) + continue; - nfound++; + nfound++; - newflags = (dp->flags & modifiers->mask) | modifiers->flags; - if (newflags == dp->flags) - continue; + newflags = (dp->flags & modifiers->mask) | modifiers->flags; + if (newflags == dp->flags) + continue; #ifdef CONFIG_JUMP_LABEL - if (dp->flags & _DPRINTK_FLAGS_PRINT) { - if (!(modifiers->flags & _DPRINTK_FLAGS_PRINT)) - static_branch_disable(&dp->key.dd_key_true); - } else if (modifiers->flags & _DPRINTK_FLAGS_PRINT) - static_branch_enable(&dp->key.dd_key_true); + if (dp->flags & _DPRINTK_FLAGS_PRINT) { + if (!(modifiers->flags & _DPRINTK_FLAGS_PRINT)) + static_branch_disable(&dp->key.dd_key_true); + } else if (modifiers->flags & _DPRINTK_FLAGS_PRINT) + static_branch_enable(&dp->key.dd_key_true); #endif - dp->flags = newflags; - v4pr_info("changed %s:%d [%s]%s =%s\n", - trim_prefix(dp->filename), dp->lineno, - dt->mod_name, dp->function, - ddebug_describe_flags(dp->flags, &fbuf)); - } + dp->flags = newflags; + v4pr_info("changed %s:%d [%s]%s =%s\n", + trim_prefix(dp->filename), dp->lineno, + dp->modname, dp->function, + ddebug_describe_flags(dp->flags, &fbuf)); } - mutex_unlock(&ddebug_lock); + codetag_lock_module_list(cttype, false); if (!nfound && verbose) pr_info("no matches for query\n"); @@ -794,187 +810,96 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, return len; } -/* - * Set the iterator to point to the first _ddebug object - * and return a pointer to that first object. Returns - * NULL if there are no _ddebugs at all. - */ -static struct _ddebug *ddebug_iter_first(struct ddebug_iter *iter) -{ - if (list_empty(&ddebug_tables)) { - iter->table = NULL; - iter->idx = 0; - return NULL; - } - iter->table = list_entry(ddebug_tables.next, - struct ddebug_table, link); - iter->idx = 0; - return &iter->table->ddebugs[iter->idx]; -} - -/* - * Advance the iterator to point to the next _ddebug - * object from the one the iterator currently points at, - * and returns a pointer to the new _ddebug. Returns - * NULL if the iterator has seen all the _ddebugs. - */ -static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter) -{ - if (iter->table == NULL) - return NULL; - if (++iter->idx == iter->table->num_ddebugs) { - /* iterate to next table */ - iter->idx = 0; - if (list_is_last(&iter->table->link, &ddebug_tables)) { - iter->table = NULL; - return NULL; - } - iter->table = list_entry(iter->table->link.next, - struct ddebug_table, link); - } - return &iter->table->ddebugs[iter->idx]; -} - -/* - * Seq_ops start method. Called at the start of every - * read() call from userspace. Takes the ddebug_lock and - * seeks the seq_file's iterator to the given position. - */ -static void *ddebug_proc_start(struct seq_file *m, loff_t *pos) -{ - struct ddebug_iter *iter = m->private; - struct _ddebug *dp; - int n = *pos; - - mutex_lock(&ddebug_lock); - - if (!n) - return SEQ_START_TOKEN; - if (n < 0) - return NULL; - dp = ddebug_iter_first(iter); - while (dp != NULL && --n > 0) - dp = ddebug_iter_next(iter); - return dp; -} - -/* - * Seq_ops next method. Called several times within a read() - * call from userspace, with ddebug_lock held. Walks to the - * next _ddebug object with a special case for the header line. - */ -static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos) -{ - struct ddebug_iter *iter = m->private; - struct _ddebug *dp; - - if (p == SEQ_START_TOKEN) - dp = ddebug_iter_first(iter); - else - dp = ddebug_iter_next(iter); - ++*pos; - return dp; -} - /* * Seq_ops show method. Called several times within a read() * call from userspace, with ddebug_lock held. Formats the * current _ddebug as a single human-readable line, with a * special case for the header line. */ -static int ddebug_proc_show(struct seq_file *m, void *p) +static void ddebug_to_text(struct seq_buf *out, struct _ddebug *dp) { - struct ddebug_iter *iter = m->private; - struct _ddebug *dp = p; struct flagsbuf flags; + char *buf; + size_t len; - if (p == SEQ_START_TOKEN) { - seq_puts(m, - "# filename:lineno [module]function flags format\n"); - return 0; - } - - seq_printf(m, "%s:%u [%s]%s =%s \"", + seq_buf_printf(out, "%s:%u [%s]%s =%s \"", trim_prefix(dp->filename), dp->lineno, - iter->table->mod_name, dp->function, + dp->modname, dp->function, ddebug_describe_flags(dp->flags, &flags)); - seq_escape(m, dp->format, "\t\r\n\""); - seq_puts(m, "\"\n"); - return 0; -} + len = seq_buf_get_buf(out, &buf); + len = string_escape_mem(dp->format, strlen(dp->format), + buf, len, ESCAPE_OCTAL, "\t\r\n\""); + seq_buf_commit(out, len); -/* - * Seq_ops stop method. Called at the end of each read() - * call from userspace. Drops ddebug_lock. - */ -static void ddebug_proc_stop(struct seq_file *m, void *p) -{ - mutex_unlock(&ddebug_lock); + seq_buf_puts(out, "\"\n"); } -static const struct seq_operations ddebug_proc_seqops = { - .start = ddebug_proc_start, - .next = ddebug_proc_next, - .show = ddebug_proc_show, - .stop = ddebug_proc_stop -}; - static int ddebug_proc_open(struct inode *inode, struct file *file) { - return seq_open_private(file, &ddebug_proc_seqops, - sizeof(struct ddebug_iter)); + struct ddebug_iter *iter; + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + codetag_lock_module_list(cttype, true); + codetag_init_iter(&iter->ct_iter, cttype); + codetag_lock_module_list(cttype, false); + seq_buf_init(&iter->buf, iter->rawbuf, sizeof(iter->rawbuf)); + file->private_data = iter; + + return 0; } -static const struct file_operations ddebug_proc_fops = { - .owner = THIS_MODULE, - .open = ddebug_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, - .write = ddebug_proc_write -}; +static int ddebug_proc_release(struct inode *inode, struct file *file) +{ + struct ddebug_iter *iter = file->private_data; -static const struct proc_ops proc_fops = { - .proc_open = ddebug_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = seq_release_private, - .proc_write = ddebug_proc_write -}; + kfree(iter); + return 0; +} -/* - * Allocate a new ddebug_table for the given module - * and add it to the global list. - */ -int ddebug_add_module(struct _ddebug *tab, unsigned int n, - const char *name) +static ssize_t ddebug_proc_read(struct file *file, char __user *ubuf, + size_t size, loff_t *ppos) { - struct ddebug_table *dt; + struct ddebug_iter *iter = file->private_data; + struct user_buf buf = { .buf = ubuf, .size = size }; + struct codetag *ct; + int err = 0; - dt = kzalloc(sizeof(*dt), GFP_KERNEL); - if (dt == NULL) { - pr_err("error adding module: %s\n", name); - return -ENOMEM; - } - /* - * For built-in modules, name lives in .rodata and is - * immortal. For loaded modules, name points at the name[] - * member of struct module, which lives at least as long as - * this struct ddebug_table. - */ - dt->mod_name = name; - dt->num_ddebugs = n; - dt->ddebugs = tab; + codetag_lock_module_list(iter->ct_iter.cttype, true); + while (1) { + err = flush_ubuf(&buf, &iter->buf); + if (err || !buf.size) + break; + + ct = codetag_next_ct(&iter->ct_iter); + if (!ct) + break; - mutex_lock(&ddebug_lock); - list_add(&dt->link, &ddebug_tables); - mutex_unlock(&ddebug_lock); + ddebug_to_text(&iter->buf, (void *) ct); + } + codetag_lock_module_list(iter->ct_iter.cttype, false); - vpr_info("%3u debug prints in module %s\n", n, dt->mod_name); - return 0; + return err ? : buf.ret; } +static const struct file_operations ddebug_proc_fops = { + .owner = THIS_MODULE, + .open = ddebug_proc_open, + .read = ddebug_proc_read, + .release = ddebug_proc_release, + .write = ddebug_proc_write, +}; + +static const struct proc_ops proc_fops = { + .proc_open = ddebug_proc_open, + .proc_read = ddebug_proc_read, + .proc_release = ddebug_proc_release, + .proc_write = ddebug_proc_write, +}; + /* helper for ddebug_dyndbg_(boot|module)_param_cb */ static int ddebug_dyndbg_param_cb(char *param, char *val, const char *modname, int on_err) @@ -1015,47 +940,6 @@ int ddebug_dyndbg_module_param_cb(char *param, char *val, const char *module) return ddebug_dyndbg_param_cb(param, val, module, -ENOENT); } -static void ddebug_table_free(struct ddebug_table *dt) -{ - list_del_init(&dt->link); - kfree(dt); -} - -/* - * Called in response to a module being unloaded. Removes - * any ddebug_table's which point at the module. - */ -int ddebug_remove_module(const char *mod_name) -{ - struct ddebug_table *dt, *nextdt; - int ret = -ENOENT; - - mutex_lock(&ddebug_lock); - list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { - if (dt->mod_name == mod_name) { - ddebug_table_free(dt); - ret = 0; - break; - } - } - mutex_unlock(&ddebug_lock); - if (!ret) - v2pr_info("removed module \"%s\"\n", mod_name); - return ret; -} - -static void ddebug_remove_all_tables(void) -{ - mutex_lock(&ddebug_lock); - while (!list_empty(&ddebug_tables)) { - struct ddebug_table *dt = list_entry(ddebug_tables.next, - struct ddebug_table, - link); - ddebug_table_free(dt); - } - mutex_unlock(&ddebug_lock); -} - static __initdata int ddebug_init_success; static int __init dynamic_debug_init_control(void) @@ -1083,45 +967,19 @@ static int __init dynamic_debug_init_control(void) static int __init dynamic_debug_init(void) { - struct _ddebug *iter, *iter_start; - const char *modname = NULL; + const struct codetag_type_desc desc = { + .section = "dyndbg", + .tag_size = sizeof(struct _ddebug), + }; char *cmdline; - int ret = 0; - int n = 0, entries = 0, modct = 0; + int ret; - if (&__start___dyndbg == &__stop___dyndbg) { - if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG)) { - pr_warn("_ddebug table is empty in a CONFIG_DYNAMIC_DEBUG build\n"); - return 1; - } - pr_info("Ignore empty _ddebug table in a CONFIG_DYNAMIC_DEBUG_CORE build\n"); - ddebug_init_success = 1; - return 0; - } - iter = __start___dyndbg; - modname = iter->modname; - iter_start = iter; - for (; iter < __stop___dyndbg; iter++) { - entries++; - if (strcmp(modname, iter->modname)) { - modct++; - ret = ddebug_add_module(iter_start, n, modname); - if (ret) - goto out_err; - n = 0; - modname = iter->modname; - iter_start = iter; - } - n++; - } - ret = ddebug_add_module(iter_start, n, modname); + cttype = codetag_register_type(&desc); + ret = PTR_ERR_OR_ZERO(cttype); if (ret) - goto out_err; + return ret; ddebug_init_success = 1; - vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg section\n", - entries, modct, (int)((modct * sizeof(struct ddebug_table)) >> 10), - (int)((entries * sizeof(struct _ddebug)) >> 10)); /* now that ddebug tables are loaded, process all boot args * again to find and activate queries given in dyndbg params. @@ -1132,14 +990,12 @@ static int __init dynamic_debug_init(void) * slightly noisy if verbose, but harmless. */ cmdline = kstrdup(saved_command_line, GFP_KERNEL); + if (!cmdline) + return -ENOMEM; parse_args("dyndbg params", cmdline, NULL, 0, 0, 0, NULL, &ddebug_dyndbg_boot_param_cb); kfree(cmdline); return 0; - -out_err: - ddebug_remove_all_tables(); - return 0; } /* Allow early initialization for boot messages via boot param */ early_initcall(dynamic_debug_init); -- 2.37.2.672.g94769d06f0-goog