* File_ops->write method for <debugfs>/dynamic_debug/control. Gathers the * command text from userspace, parses and executes it. */#define USER_BUF_PAGE 4096static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp){ char *tmpbuf; int ret; if (len == 0) return 0; if (len > USER_BUF_PAGE - 1) { pr_warn("expected <%d bytes into control\n", USER_BUF_PAGE); return -E2BIG; } tmpbuf = memdup_user_nul(ubuf, len); if (IS_ERR(tmpbuf)) return PTR_ERR(tmpbuf); v2pr_info("read %zu bytes from userspace\n", len); ret = ddebug_exec_queries(tmpbuf, NULL); kfree(tmpbuf); if (ret < 0) return ret; *offp += len; 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; return NULL; } iter->table = list_entry(ddebug_tables.next, struct ddebug_table, link); iter->idx = iter->table->num_ddebugs; 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 < 0) { /* iterate to next table */ 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); iter->idx = iter->table->num_ddebugs; --iter->idx; } 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;} #define class_in_range(class_id, map) \ (class_id >= map->base && class_id < map->base + map->length) static const char *ddebug_class_name(struct ddebug_iter *iter, struct _ddebug *dp){ struct ddebug_class_map *map; list_for_each_entry(map, &iter->table->maps, link) if (class_in_range(dp->class_id, map)) return map->class_names[dp->class_id - map->base]; return NULL;} /* * 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){ struct ddebug_iter *iter = m->private; struct _ddebug *dp = p; struct flagsbuf flags; char const *class; 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 \"", trim_prefix(dp->filename), dp->lineno, iter->table->mod_name, dp->function, ddebug_describe_flags(dp->flags, &flags)); seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\""); seq_putc(m, '"'); if (dp->class_id != _DPRINTK_CLASS_DFLT) { class = ddebug_class_name(iter, dp); if (class) seq_printf(m, " class:%s", class); else seq_printf(m, " class unknown, _id:%d", dp->class_id); } seq_putc(m, '\n'); return 0;} /* * 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);} 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));} 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 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}; static void ddebug_attach_module_classes(struct ddebug_table *dt, struct ddebug_class_map *classes, int num_classes){ struct ddebug_class_map *cm; int i, j, ct = 0; for (cm = classes, i = 0; i < num_classes; i++, cm++) { if (!strcmp(cm->mod_name, dt->mod_name)) { v2pr_info("class[%d]: module:%s base:%d len:%d ty:%d\n", i, cm->mod_name, cm->base, cm->length, cm->map_type); for (j = 0; j < cm->length; j++) v3pr_info(" %d: %d %s\n", j + cm->base, j, cm->class_names[j]); list_add(&cm->link, &dt->maps); ct++; } } if (ct) vpr_info("module:%s attached %d classes\n", dt->mod_name, ct);} /* * Allocate a new ddebug_table for the given module * and add it to the global list. */static int ddebug_add_module(struct _ddebug_info *di, const char *modname){ struct ddebug_table *dt; v3pr_info("add-module: %s.%d sites\n", modname, di->num_descs); if (!di->num_descs) { v3pr_info(" skip %s\n", modname); return 0; } dt = kzalloc_obj(*dt); if (dt == NULL) { pr_err("error adding module: %s\n", modname); 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 = modname; dt->ddebugs = di->descs; dt->num_ddebugs = di->num_descs; INIT_LIST_HEAD(&dt->link); INIT_LIST_HEAD(&dt->maps); if (di->classes && di->num_classes) ddebug_attach_module_classes(dt, di->classes, di->num_classes); mutex_lock(&ddebug_lock); list_add_tail(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); vpr_info("%3u debug prints in module %s\n", di->num_descs, modname); return 0;} /* helper for ddebug_dyndbg_(boot|module)_param_cb */static int ddebug_dyndbg_param_cb(char *param, char *val, const char *modname, int on_err){ char *sep; sep = strchr(param, '.'); if (sep) { /* needed only for ddebug_dyndbg_boot_param_cb */ *sep = '\0'; modname = param; param = sep + 1; } if (strcmp(param, "dyndbg")) return on_err; /* determined by caller */ ddebug_exec_queries((val ? val : "+p"), modname); return 0; /* query failure shouldn't stop module load */} /* handle both dyndbg and $module.dyndbg params at boot */static int ddebug_dyndbg_boot_param_cb(char *param, char *val, const char *unused, void *arg){ vpr_info("%s=\"%s\"\n", param, val); return ddebug_dyndbg_param_cb(param, val, NULL, 0);} /* * modprobe foo finds foo.params in boot-args, strips "foo.", and * passes them to load_module(). This callback gets unknown params, * processes dyndbg params, rejects others. */int ddebug_dyndbg_module_param_cb(char *param, char *val, const char *module){ vpr_info("module: %s %s=\"%s\"\n", module, param, val); 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);} #ifdef CONFIG_MODULES /* * Called in response to a module being unloaded. Removes * any ddebug_table's which point at the module. */static 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 int ddebug_module_notify(struct notifier_block *self, unsigned long val, void *data){ struct module *mod = data; int ret = 0; switch (val) { case MODULE_STATE_COMING: ret = ddebug_add_module(&mod->dyndbg_info, mod->name); if (ret) WARN(1, "Failed to allocate memory: dyndbg may not work properly.\n"); break; case MODULE_STATE_GOING: ddebug_remove_module(mod->name); break; } return notifier_from_errno(ret);} static struct notifier_block ddebug_module_nb = { .notifier_call = ddebug_module_notify, .priority = 0, /* dynamic debug depends on jump label */}; #endif /* CONFIG_MODULES */ 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){ struct proc_dir_entry *procfs_dir; struct dentry *debugfs_dir; if (!ddebug_init_success) return -ENODEV; /* Create the control file in debugfs if it is enabled */ if (debugfs_initialized()) { debugfs_dir = debugfs_create_dir("dynamic_debug", NULL); debugfs_create_file("control", 0644, debugfs_dir, NULL, &ddebug_proc_fops); } /* Also create the control file in procfs */ procfs_dir = proc_mkdir("dynamic_debug", NULL); if (procfs_dir) proc_create("control", 0644, procfs_dir, &proc_fops); return 0;} static int __init dynamic_debug_init(void){ struct _ddebug *iter, *iter_mod_start; int ret, i, mod_sites, mod_ct; const char *modname; char *cmdline; struct _ddebug_info di = { .descs = __start___dyndbg, .classes = __start___dyndbg_classes, .num_descs = __stop___dyndbg - __start___dyndbg, .num_classes = __stop___dyndbg_classes - __start___dyndbg_classes, }; #ifdef CONFIG_MODULES ret = register_module_notifier(&ddebug_module_nb); if (ret) { pr_warn("Failed to register dynamic debug module notifier\n"); return ret; }#endif /* CONFIG_MODULES */ 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 = iter_mod_start = __start___dyndbg; modname = iter->modname; i = mod_sites = mod_ct = 0; for (; iter < __stop___dyndbg; iter++, i++, mod_sites++) { if (strcmp(modname, iter->modname)) { mod_ct++; di.num_descs = mod_sites; di.descs = iter_mod_start; ret = ddebug_add_module(&di, modname); if (ret) goto out_err; mod_sites = 0; modname = iter->modname; iter_mod_start = iter; } } di.num_descs = mod_sites; di.descs = iter_mod_start; ret = ddebug_add_module(&di, modname); if (ret) goto out_err; ddebug_init_success = 1; vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg section\n", i, mod_ct, (int)((mod_ct * sizeof(struct ddebug_table)) >> 10), (int)((i * sizeof(struct _ddebug)) >> 10)); if (di.num_classes) v2pr_info(" %d builtin ddebug class-maps\n", di.num_classes); /* now that ddebug tables are loaded, process all boot args * again to find and activate queries given in dyndbg params. * While this has already been done for known boot params, it * ignored the unknown ones (dyndbg in particular). Reusing * parse_args avoids ad-hoc parsing. This will also attempt * to activate queries for not-yet-loaded modules, which is * slightly noisy if verbose, but harmless. */ cmdline = kstrdup(saved_command_line, GFP_KERNEL); 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); /* Debugfs setup must be done later */fs_initcall(dynamic_debug_init_control);Lines 1001–1495 of 1495. Previous lines
* File_ops->write method for <debugfs>/dynamic_debug/control. Gathers the * command text from userspace, parses and executes it. */ #define USER_BUF_PAGE 4096 static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { char *tmpbuf; int ret; if (len == 0) return 0; if (len > USER_BUF_PAGE - 1) { pr_warn("expected <%d bytes into control\n", USER_BUF_PAGE); return -E2BIG; } tmpbuf = memdup_user_nul(ubuf, len); if (IS_ERR(tmpbuf)) return PTR_ERR(tmpbuf); v2pr_info("read %zu bytes from userspace\n", len); ret = ddebug_exec_queries(tmpbuf, NULL); kfree(tmpbuf); if (ret < 0) return ret; *offp += len; 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; return NULL; } iter->table = list_entry(ddebug_tables.next, struct ddebug_table, link); iter->idx = iter->table->num_ddebugs; 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 < 0) { /* iterate to next table */ 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); iter->idx = iter->table->num_ddebugs; --iter->idx; } 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; } #define class_in_range(class_id, map) \ (class_id >= map->base && class_id < map->base + map->length) static const char *ddebug_class_name(struct ddebug_iter *iter, struct _ddebug *dp) { struct ddebug_class_map *map; list_for_each_entry(map, &iter->table->maps, link) if (class_in_range(dp->class_id, map)) return map->class_names[dp->class_id - map->base]; return NULL; } /* * 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) { struct ddebug_iter *iter = m->private; struct _ddebug *dp = p; struct flagsbuf flags; char const *class; 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 \"", trim_prefix(dp->filename), dp->lineno, iter->table->mod_name, dp->function, ddebug_describe_flags(dp->flags, &flags)); seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\""); seq_putc(m, '"'); if (dp->class_id != _DPRINTK_CLASS_DFLT) { class = ddebug_class_name(iter, dp); if (class) seq_printf(m, " class:%s", class); else seq_printf(m, " class unknown, _id:%d", dp->class_id); } seq_putc(m, '\n'); return 0; } /* * 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); } 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)); } 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 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 }; static void ddebug_attach_module_classes(struct ddebug_table *dt, struct ddebug_class_map *classes, int num_classes) { struct ddebug_class_map *cm; int i, j, ct = 0; for (cm = classes, i = 0; i < num_classes; i++, cm++) { if (!strcmp(cm->mod_name, dt->mod_name)) { v2pr_info("class[%d]: module:%s base:%d len:%d ty:%d\n", i, cm->mod_name, cm->base, cm->length, cm->map_type); for (j = 0; j < cm->length; j++) v3pr_info(" %d: %d %s\n", j + cm->base, j, cm->class_names[j]); list_add(&cm->link, &dt->maps); ct++; } } if (ct) vpr_info("module:%s attached %d classes\n", dt->mod_name, ct); } /* * Allocate a new ddebug_table for the given module * and add it to the global list. */ static int ddebug_add_module(struct _ddebug_info *di, const char *modname) { struct ddebug_table *dt; v3pr_info("add-module: %s.%d sites\n", modname, di->num_descs); if (!di->num_descs) { v3pr_info(" skip %s\n", modname); return 0; } dt = kzalloc_obj(*dt); if (dt == NULL) { pr_err("error adding module: %s\n", modname); 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 = modname; dt->ddebugs = di->descs; dt->num_ddebugs = di->num_descs; INIT_LIST_HEAD(&dt->link); INIT_LIST_HEAD(&dt->maps); if (di->classes && di->num_classes) ddebug_attach_module_classes(dt, di->classes, di->num_classes); mutex_lock(&ddebug_lock); list_add_tail(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); vpr_info("%3u debug prints in module %s\n", di->num_descs, modname); return 0; } /* helper for ddebug_dyndbg_(boot|module)_param_cb */ static int ddebug_dyndbg_param_cb(char *param, char *val, const char *modname, int on_err) { char *sep; sep = strchr(param, '.'); if (sep) { /* needed only for ddebug_dyndbg_boot_param_cb */ *sep = '\0'; modname = param; param = sep + 1; } if (strcmp(param, "dyndbg")) return on_err; /* determined by caller */ ddebug_exec_queries((val ? val : "+p"), modname); return 0; /* query failure shouldn't stop module load */ } /* handle both dyndbg and $module.dyndbg params at boot */ static int ddebug_dyndbg_boot_param_cb(char *param, char *val, const char *unused, void *arg) { vpr_info("%s=\"%s\"\n", param, val); return ddebug_dyndbg_param_cb(param, val, NULL, 0); } /* * modprobe foo finds foo.params in boot-args, strips "foo.", and * passes them to load_module(). This callback gets unknown params, * processes dyndbg params, rejects others. */ int ddebug_dyndbg_module_param_cb(char *param, char *val, const char *module) { vpr_info("module: %s %s=\"%s\"\n", module, param, val); 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); } #ifdef CONFIG_MODULES /* * Called in response to a module being unloaded. Removes * any ddebug_table's which point at the module. */ static 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 int ddebug_module_notify(struct notifier_block *self, unsigned long val, void *data) { struct module *mod = data; int ret = 0; switch (val) { case MODULE_STATE_COMING: ret = ddebug_add_module(&mod->dyndbg_info, mod->name); if (ret) WARN(1, "Failed to allocate memory: dyndbg may not work properly.\n"); break; case MODULE_STATE_GOING: ddebug_remove_module(mod->name); break; } return notifier_from_errno(ret); } static struct notifier_block ddebug_module_nb = { .notifier_call = ddebug_module_notify, .priority = 0, /* dynamic debug depends on jump label */ }; #endif /* CONFIG_MODULES */ 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) { struct proc_dir_entry *procfs_dir; struct dentry *debugfs_dir; if (!ddebug_init_success) return -ENODEV; /* Create the control file in debugfs if it is enabled */ if (debugfs_initialized()) { debugfs_dir = debugfs_create_dir("dynamic_debug", NULL); debugfs_create_file("control", 0644, debugfs_dir, NULL, &ddebug_proc_fops); } /* Also create the control file in procfs */ procfs_dir = proc_mkdir("dynamic_debug", NULL); if (procfs_dir) proc_create("control", 0644, procfs_dir, &proc_fops); return 0; } static int __init dynamic_debug_init(void) { struct _ddebug *iter, *iter_mod_start; int ret, i, mod_sites, mod_ct; const char *modname; char *cmdline; struct _ddebug_info di = { .descs = __start___dyndbg, .classes = __start___dyndbg_classes, .num_descs = __stop___dyndbg - __start___dyndbg, .num_classes = __stop___dyndbg_classes - __start___dyndbg_classes, }; #ifdef CONFIG_MODULES ret = register_module_notifier(&ddebug_module_nb); if (ret) { pr_warn("Failed to register dynamic debug module notifier\n"); return ret; } #endif /* CONFIG_MODULES */ 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 = iter_mod_start = __start___dyndbg; modname = iter->modname; i = mod_sites = mod_ct = 0; for (; iter < __stop___dyndbg; iter++, i++, mod_sites++) { if (strcmp(modname, iter->modname)) { mod_ct++; di.num_descs = mod_sites; di.descs = iter_mod_start; ret = ddebug_add_module(&di, modname); if (ret) goto out_err; mod_sites = 0; modname = iter->modname; iter_mod_start = iter; } } di.num_descs = mod_sites; di.descs = iter_mod_start; ret = ddebug_add_module(&di, modname); if (ret) goto out_err; ddebug_init_success = 1; vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg section\n", i, mod_ct, (int)((mod_ct * sizeof(struct ddebug_table)) >> 10), (int)((i * sizeof(struct _ddebug)) >> 10)); if (di.num_classes) v2pr_info(" %d builtin ddebug class-maps\n", di.num_classes); /* now that ddebug tables are loaded, process all boot args * again to find and activate queries given in dyndbg params. * While this has already been done for known boot params, it * ignored the unknown ones (dyndbg in particular). Reusing * parse_args avoids ad-hoc parsing. This will also attempt * to activate queries for not-yet-loaded modules, which is * slightly noisy if verbose, but harmless. */ cmdline = kstrdup(saved_command_line, GFP_KERNEL); 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); /* Debugfs setup must be done later */ fs_initcall(dynamic_debug_init_control);