aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dynamic_debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dynamic_debug.c')
-rw-r--r--lib/dynamic_debug.c154
1 files changed, 83 insertions, 71 deletions
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 833139ce1e22..b335acb43be2 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -21,22 +21,16 @@
21#include <linux/list.h> 21#include <linux/list.h>
22#include <linux/sysctl.h> 22#include <linux/sysctl.h>
23#include <linux/ctype.h> 23#include <linux/ctype.h>
24#include <linux/string.h>
24#include <linux/uaccess.h> 25#include <linux/uaccess.h>
25#include <linux/dynamic_debug.h> 26#include <linux/dynamic_debug.h>
26#include <linux/debugfs.h> 27#include <linux/debugfs.h>
28#include <linux/slab.h>
29#include <linux/jump_label.h>
27 30
28extern struct _ddebug __start___verbose[]; 31extern struct _ddebug __start___verbose[];
29extern struct _ddebug __stop___verbose[]; 32extern struct _ddebug __stop___verbose[];
30 33
31/* dynamic_debug_enabled, and dynamic_debug_enabled2 are bitmasks in which
32 * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They
33 * use independent hash functions, to reduce the chance of false positives.
34 */
35long long dynamic_debug_enabled;
36EXPORT_SYMBOL_GPL(dynamic_debug_enabled);
37long long dynamic_debug_enabled2;
38EXPORT_SYMBOL_GPL(dynamic_debug_enabled2);
39
40struct ddebug_table { 34struct ddebug_table {
41 struct list_head link; 35 struct list_head link;
42 char *mod_name; 36 char *mod_name;
@@ -86,26 +80,6 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf,
86} 80}
87 81
88/* 82/*
89 * must be called with ddebug_lock held
90 */
91
92static int disabled_hash(char hash, bool first_table)
93{
94 struct ddebug_table *dt;
95 char table_hash_value;
96
97 list_for_each_entry(dt, &ddebug_tables, link) {
98 if (first_table)
99 table_hash_value = dt->ddebugs->primary_hash;
100 else
101 table_hash_value = dt->ddebugs->secondary_hash;
102 if (dt->num_enabled && (hash == table_hash_value))
103 return 0;
104 }
105 return 1;
106}
107
108/*
109 * Search the tables for _ddebug's which match the given 83 * Search the tables for _ddebug's which match the given
110 * `query' and apply the `flags' and `mask' to them. Tells 84 * `query' and apply the `flags' and `mask' to them. Tells
111 * the user which ddebug's were changed, or whether none 85 * the user which ddebug's were changed, or whether none
@@ -164,22 +138,13 @@ static void ddebug_change(const struct ddebug_query *query,
164 138
165 if (!newflags) 139 if (!newflags)
166 dt->num_enabled--; 140 dt->num_enabled--;
167 else if (!dp-flags) 141 else if (!dp->flags)
168 dt->num_enabled++; 142 dt->num_enabled++;
169 dp->flags = newflags; 143 dp->flags = newflags;
170 if (newflags) { 144 if (newflags)
171 dynamic_debug_enabled |= 145 dp->enabled = 1;
172 (1LL << dp->primary_hash); 146 else
173 dynamic_debug_enabled2 |= 147 dp->enabled = 0;
174 (1LL << dp->secondary_hash);
175 } else {
176 if (disabled_hash(dp->primary_hash, true))
177 dynamic_debug_enabled &=
178 ~(1LL << dp->primary_hash);
179 if (disabled_hash(dp->secondary_hash, false))
180 dynamic_debug_enabled2 &=
181 ~(1LL << dp->secondary_hash);
182 }
183 if (verbose) 148 if (verbose)
184 printk(KERN_INFO 149 printk(KERN_INFO
185 "ddebug: changed %s:%d [%s]%s %s\n", 150 "ddebug: changed %s:%d [%s]%s %s\n",
@@ -209,8 +174,7 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords)
209 char *end; 174 char *end;
210 175
211 /* Skip leading whitespace */ 176 /* Skip leading whitespace */
212 while (*buf && isspace(*buf)) 177 buf = skip_spaces(buf);
213 buf++;
214 if (!*buf) 178 if (!*buf)
215 break; /* oh, it was trailing whitespace */ 179 break; /* oh, it was trailing whitespace */
216 180
@@ -428,6 +392,40 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
428 return 0; 392 return 0;
429} 393}
430 394
395static int ddebug_exec_query(char *query_string)
396{
397 unsigned int flags = 0, mask = 0;
398 struct ddebug_query query;
399#define MAXWORDS 9
400 int nwords;
401 char *words[MAXWORDS];
402
403 nwords = ddebug_tokenize(query_string, words, MAXWORDS);
404 if (nwords <= 0)
405 return -EINVAL;
406 if (ddebug_parse_query(words, nwords-1, &query))
407 return -EINVAL;
408 if (ddebug_parse_flags(words[nwords-1], &flags, &mask))
409 return -EINVAL;
410
411 /* actually go and implement the change */
412 ddebug_change(&query, flags, mask);
413 return 0;
414}
415
416static __initdata char ddebug_setup_string[1024];
417static __init int ddebug_setup_query(char *str)
418{
419 if (strlen(str) >= 1024) {
420 pr_warning("ddebug boot param string too large\n");
421 return 0;
422 }
423 strcpy(ddebug_setup_string, str);
424 return 1;
425}
426
427__setup("ddebug_query=", ddebug_setup_query);
428
431/* 429/*
432 * File_ops->write method for <debugfs>/dynamic_debug/conrol. Gathers the 430 * File_ops->write method for <debugfs>/dynamic_debug/conrol. Gathers the
433 * command text from userspace, parses and executes it. 431 * command text from userspace, parses and executes it.
@@ -435,12 +433,8 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
435static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, 433static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
436 size_t len, loff_t *offp) 434 size_t len, loff_t *offp)
437{ 435{
438 unsigned int flags = 0, mask = 0;
439 struct ddebug_query query;
440#define MAXWORDS 9
441 int nwords;
442 char *words[MAXWORDS];
443 char tmpbuf[256]; 436 char tmpbuf[256];
437 int ret;
444 438
445 if (len == 0) 439 if (len == 0)
446 return 0; 440 return 0;
@@ -454,16 +448,9 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
454 printk(KERN_INFO "%s: read %d bytes from userspace\n", 448 printk(KERN_INFO "%s: read %d bytes from userspace\n",
455 __func__, (int)len); 449 __func__, (int)len);
456 450
457 nwords = ddebug_tokenize(tmpbuf, words, MAXWORDS); 451 ret = ddebug_exec_query(tmpbuf);
458 if (nwords < 0) 452 if (ret)
459 return -EINVAL; 453 return ret;
460 if (ddebug_parse_query(words, nwords-1, &query))
461 return -EINVAL;
462 if (ddebug_parse_flags(words[nwords-1], &flags, &mask))
463 return -EINVAL;
464
465 /* actually go and implement the change */
466 ddebug_change(&query, flags, mask);
467 454
468 *offp += len; 455 *offp += len;
469 return len; 456 return len;
@@ -691,7 +678,7 @@ static void ddebug_table_free(struct ddebug_table *dt)
691 * Called in response to a module being unloaded. Removes 678 * Called in response to a module being unloaded. Removes
692 * any ddebug_table's which point at the module. 679 * any ddebug_table's which point at the module.
693 */ 680 */
694int ddebug_remove_module(char *mod_name) 681int ddebug_remove_module(const char *mod_name)
695{ 682{
696 struct ddebug_table *dt, *nextdt; 683 struct ddebug_table *dt, *nextdt;
697 int ret = -ENOENT; 684 int ret = -ENOENT;
@@ -724,13 +711,14 @@ static void ddebug_remove_all_tables(void)
724 mutex_unlock(&ddebug_lock); 711 mutex_unlock(&ddebug_lock);
725} 712}
726 713
727static int __init dynamic_debug_init(void) 714static __initdata int ddebug_init_success;
715
716static int __init dynamic_debug_init_debugfs(void)
728{ 717{
729 struct dentry *dir, *file; 718 struct dentry *dir, *file;
730 struct _ddebug *iter, *iter_start; 719
731 const char *modname = NULL; 720 if (!ddebug_init_success)
732 int ret = 0; 721 return -ENODEV;
733 int n = 0;
734 722
735 dir = debugfs_create_dir("dynamic_debug", NULL); 723 dir = debugfs_create_dir("dynamic_debug", NULL);
736 if (!dir) 724 if (!dir)
@@ -741,6 +729,16 @@ static int __init dynamic_debug_init(void)
741 debugfs_remove(dir); 729 debugfs_remove(dir);
742 return -ENOMEM; 730 return -ENOMEM;
743 } 731 }
732 return 0;
733}
734
735static int __init dynamic_debug_init(void)
736{
737 struct _ddebug *iter, *iter_start;
738 const char *modname = NULL;
739 int ret = 0;
740 int n = 0;
741
744 if (__start___verbose != __stop___verbose) { 742 if (__start___verbose != __stop___verbose) {
745 iter = __start___verbose; 743 iter = __start___verbose;
746 modname = iter->modname; 744 modname = iter->modname;
@@ -758,12 +756,26 @@ static int __init dynamic_debug_init(void)
758 } 756 }
759 ret = ddebug_add_module(iter_start, n, modname); 757 ret = ddebug_add_module(iter_start, n, modname);
760 } 758 }
759
760 /* ddebug_query boot param got passed -> set it up */
761 if (ddebug_setup_string[0] != '\0') {
762 ret = ddebug_exec_query(ddebug_setup_string);
763 if (ret)
764 pr_warning("Invalid ddebug boot param %s",
765 ddebug_setup_string);
766 else
767 pr_info("ddebug initialized with string %s",
768 ddebug_setup_string);
769 }
770
761out_free: 771out_free:
762 if (ret) { 772 if (ret)
763 ddebug_remove_all_tables(); 773 ddebug_remove_all_tables();
764 debugfs_remove(dir); 774 else
765 debugfs_remove(file); 775 ddebug_init_success = 1;
766 }
767 return 0; 776 return 0;
768} 777}
769module_init(dynamic_debug_init); 778/* Allow early initialization for boot messages via boot param */
779arch_initcall(dynamic_debug_init);
780/* Debugfs setup must be done later */
781module_init(dynamic_debug_init_debugfs);