} vpr_info("processed %d queries, with %d matches, %d errs\n", i, nfound, errs); if (exitcode) return exitcode; return nfound;} #define PREFIX_SIZE 64 static int remaining(int wrote){ if (PREFIX_SIZE - wrote > 0) return PREFIX_SIZE - wrote; return 0;} static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf){ int pos_after_tid; int pos = 0; pos += snprintf(buf + pos, remaining(pos), "%s", KERN_DEBUG); if (desc->flags & _DPRINTK_FLAGS_INCL_TID) { if (in_interrupt()) pos += snprintf(buf + pos, remaining(pos), "%s ", "<intr>"); else pos += snprintf(buf + pos, remaining(pos), "[%d] ", task_pid_vnr(current)); } pos_after_tid = pos; if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", desc->modname); if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", desc->function); if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO) pos += snprintf(buf + pos, remaining(pos), "%d:", desc->lineno); if (pos - pos_after_tid) pos += snprintf(buf + pos, remaining(pos), " "); if (pos >= PREFIX_SIZE) buf[PREFIX_SIZE - 1] = '\0'; return buf;} int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...){ va_list args; int res; struct va_format vaf; char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; res = printk("%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf); va_end(args); return res;}EXPORT_SYMBOL(__dynamic_pr_debug); int __dynamic_dev_dbg(struct _ddebug *descriptor, const struct device *dev, const char *fmt, ...){ struct va_format vaf; va_list args; int res; char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; res = __dev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf); va_end(args); return res;}EXPORT_SYMBOL(__dynamic_dev_dbg); #ifdef CONFIG_NET int __dynamic_netdev_dbg(struct _ddebug *descriptor, const struct net_device *dev, const char *fmt, ...){ struct va_format vaf; va_list args; int res; char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; res = __netdev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf); va_end(args); return res;}EXPORT_SYMBOL(__dynamic_netdev_dbg); #endif #define DDEBUG_STRING_SIZE 1024static __initdata char ddebug_setup_string[DDEBUG_STRING_SIZE]; static __init int ddebug_setup_query(char *str){ if (strlen(str) >= DDEBUG_STRING_SIZE) { pr_warn("ddebug boot param string too large\n"); return 0; } strlcpy(ddebug_setup_string, str, DDEBUG_STRING_SIZE); return 1;} __setup("ddebug_query=", ddebug_setup_query); /* * File_ops->write method for <debugfs>/dynamic_debug/conrol. 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 = kmalloc(len + 1, GFP_KERNEL); if (!tmpbuf) return -ENOMEM; if (copy_from_user(tmpbuf, ubuf, len)) { kfree(tmpbuf); return -EFAULT; } tmpbuf[len] = '\0'; vpr_info("read %d bytes from userspace\n", (int)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; 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; vpr_info("called m=%p *pos=%lld\n", m, (unsigned long long)*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; vpr_info("called m=%p p=%p *pos=%lld\n", m, p, (unsigned long long)*pos); 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){ struct ddebug_iter *iter = m->private; struct _ddebug *dp = p; char flagsbuf[10]; vpr_info("called m=%p p=%p\n", m, p); 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, flagsbuf, sizeof(flagsbuf))); seq_escape(m, dp->format, "\t\r\n\""); seq_puts(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){ vpr_info("called m=%p p=%p\n", m, 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}; /* * File_ops->open method for <debugfs>/dynamic_debug/control. Does * the seq_file setup dance, and also creates an iterator to walk the * _ddebugs. Note that we create a seq_file always, even for O_WRONLY * files where it's not needed, as doing so simplifies the ->release * method. */static int ddebug_proc_open(struct inode *inode, struct file *file){ struct ddebug_iter *iter; int err; vpr_info("called\n"); iter = kzalloc(sizeof(*iter), GFP_KERNEL); if (iter == NULL) return -ENOMEM; err = seq_open(file, &ddebug_proc_seqops); if (err) { kfree(iter); return err; } ((struct seq_file *) file->private_data)->private = 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}; /* * 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){ struct ddebug_table *dt; char *new_name; dt = kzalloc(sizeof(*dt), GFP_KERNEL); if (dt == NULL) return -ENOMEM; new_name = kstrdup(name, GFP_KERNEL); if (new_name == NULL) { kfree(dt); return -ENOMEM; } dt->mod_name = new_name; dt->num_ddebugs = n; dt->ddebugs = tab; mutex_lock(&ddebug_lock); list_add_tail(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); vpr_info("%u debug prints in module %s\n", n, dt->mod_name); return 0;}EXPORT_SYMBOL_GPL(ddebug_add_module); /* 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 shouldnt 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){ 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->mod_name); 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; vpr_info("removing module \"%s\"\n", mod_name); mutex_lock(&ddebug_lock); list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { if (!strcmp(dt->mod_name, mod_name)) { ddebug_table_free(dt); ret = 0; } } mutex_unlock(&ddebug_lock); return ret;}EXPORT_SYMBOL_GPL(ddebug_remove_module); 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_debugfs(void){ struct dentry *dir, *file; if (!ddebug_init_success) return -ENODEV; dir = debugfs_create_dir("dynamic_debug", NULL); if (!dir) return -ENOMEM; file = debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); if (!file) { debugfs_remove(dir); return -ENOMEM; } return 0;} static int __init dynamic_debug_init(void){ struct _ddebug *iter, *iter_start; const char *modname = NULL; char *cmdline; int ret = 0; int n = 0, entries = 0, modct = 0; int verbose_bytes = 0; if (__start___verbose == __stop___verbose) { pr_warn("_ddebug table is empty in a " "CONFIG_DYNAMIC_DEBUG build"); return 1; } iter = __start___verbose; modname = iter->modname; iter_start = iter; for (; iter < __stop___verbose; iter++) { entries++; verbose_bytes += strlen(iter->modname) + strlen(iter->function) + strlen(iter->filename) + strlen(iter->format); if (strcmp(modname, iter->modname)) { modct++; ret = ddebug_add_module(iter_start, n, modname); if (ret) goto out_err; n = 0; modname = iter->modname;Lines 501–1000 of 1048. Previous lines Load more
} vpr_info("processed %d queries, with %d matches, %d errs\n", i, nfound, errs); if (exitcode) return exitcode; return nfound; } #define PREFIX_SIZE 64 static int remaining(int wrote) { if (PREFIX_SIZE - wrote > 0) return PREFIX_SIZE - wrote; return 0; } static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf) { int pos_after_tid; int pos = 0; pos += snprintf(buf + pos, remaining(pos), "%s", KERN_DEBUG); if (desc->flags & _DPRINTK_FLAGS_INCL_TID) { if (in_interrupt()) pos += snprintf(buf + pos, remaining(pos), "%s ", "<intr>"); else pos += snprintf(buf + pos, remaining(pos), "[%d] ", task_pid_vnr(current)); } pos_after_tid = pos; if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", desc->modname); if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", desc->function); if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO) pos += snprintf(buf + pos, remaining(pos), "%d:", desc->lineno); if (pos - pos_after_tid) pos += snprintf(buf + pos, remaining(pos), " "); if (pos >= PREFIX_SIZE) buf[PREFIX_SIZE - 1] = '\0'; return buf; } int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) { va_list args; int res; struct va_format vaf; char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; res = printk("%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf); va_end(args); return res; } EXPORT_SYMBOL(__dynamic_pr_debug); int __dynamic_dev_dbg(struct _ddebug *descriptor, const struct device *dev, const char *fmt, ...) { struct va_format vaf; va_list args; int res; char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; res = __dev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf); va_end(args); return res; } EXPORT_SYMBOL(__dynamic_dev_dbg); #ifdef CONFIG_NET int __dynamic_netdev_dbg(struct _ddebug *descriptor, const struct net_device *dev, const char *fmt, ...) { struct va_format vaf; va_list args; int res; char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; res = __netdev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf); va_end(args); return res; } EXPORT_SYMBOL(__dynamic_netdev_dbg); #endif #define DDEBUG_STRING_SIZE 1024 static __initdata char ddebug_setup_string[DDEBUG_STRING_SIZE]; static __init int ddebug_setup_query(char *str) { if (strlen(str) >= DDEBUG_STRING_SIZE) { pr_warn("ddebug boot param string too large\n"); return 0; } strlcpy(ddebug_setup_string, str, DDEBUG_STRING_SIZE); return 1; } __setup("ddebug_query=", ddebug_setup_query); /* * File_ops->write method for <debugfs>/dynamic_debug/conrol. 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 = kmalloc(len + 1, GFP_KERNEL); if (!tmpbuf) return -ENOMEM; if (copy_from_user(tmpbuf, ubuf, len)) { kfree(tmpbuf); return -EFAULT; } tmpbuf[len] = '\0'; vpr_info("read %d bytes from userspace\n", (int)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; 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; vpr_info("called m=%p *pos=%lld\n", m, (unsigned long long)*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; vpr_info("called m=%p p=%p *pos=%lld\n", m, p, (unsigned long long)*pos); 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) { struct ddebug_iter *iter = m->private; struct _ddebug *dp = p; char flagsbuf[10]; vpr_info("called m=%p p=%p\n", m, p); 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, flagsbuf, sizeof(flagsbuf))); seq_escape(m, dp->format, "\t\r\n\""); seq_puts(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) { vpr_info("called m=%p p=%p\n", m, 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 }; /* * File_ops->open method for <debugfs>/dynamic_debug/control. Does * the seq_file setup dance, and also creates an iterator to walk the * _ddebugs. Note that we create a seq_file always, even for O_WRONLY * files where it's not needed, as doing so simplifies the ->release * method. */ static int ddebug_proc_open(struct inode *inode, struct file *file) { struct ddebug_iter *iter; int err; vpr_info("called\n"); iter = kzalloc(sizeof(*iter), GFP_KERNEL); if (iter == NULL) return -ENOMEM; err = seq_open(file, &ddebug_proc_seqops); if (err) { kfree(iter); return err; } ((struct seq_file *) file->private_data)->private = 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 }; /* * 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) { struct ddebug_table *dt; char *new_name; dt = kzalloc(sizeof(*dt), GFP_KERNEL); if (dt == NULL) return -ENOMEM; new_name = kstrdup(name, GFP_KERNEL); if (new_name == NULL) { kfree(dt); return -ENOMEM; } dt->mod_name = new_name; dt->num_ddebugs = n; dt->ddebugs = tab; mutex_lock(&ddebug_lock); list_add_tail(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); vpr_info("%u debug prints in module %s\n", n, dt->mod_name); return 0; } EXPORT_SYMBOL_GPL(ddebug_add_module); /* 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 shouldnt 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) { 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->mod_name); 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; vpr_info("removing module \"%s\"\n", mod_name); mutex_lock(&ddebug_lock); list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { if (!strcmp(dt->mod_name, mod_name)) { ddebug_table_free(dt); ret = 0; } } mutex_unlock(&ddebug_lock); return ret; } EXPORT_SYMBOL_GPL(ddebug_remove_module); 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_debugfs(void) { struct dentry *dir, *file; if (!ddebug_init_success) return -ENODEV; dir = debugfs_create_dir("dynamic_debug", NULL); if (!dir) return -ENOMEM; file = debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); if (!file) { debugfs_remove(dir); return -ENOMEM; } return 0; } static int __init dynamic_debug_init(void) { struct _ddebug *iter, *iter_start; const char *modname = NULL; char *cmdline; int ret = 0; int n = 0, entries = 0, modct = 0; int verbose_bytes = 0; if (__start___verbose == __stop___verbose) { pr_warn("_ddebug table is empty in a " "CONFIG_DYNAMIC_DEBUG build"); return 1; } iter = __start___verbose; modname = iter->modname; iter_start = iter; for (; iter < __stop___verbose; iter++) { entries++; verbose_bytes += strlen(iter->modname) + strlen(iter->function) + strlen(iter->filename) + strlen(iter->format); if (strcmp(modname, iter->modname)) { modct++; ret = ddebug_add_module(iter_start, n, modname); if (ret) goto out_err; n = 0; modname = iter->modname;