1495 lines · 37.7 KB · C
RawDownload
 * 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

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t