driver core: basic infrastructure for per-module dynamic debug messages
Jason Baron [Tue, 12 Aug 2008 20:46:19 +0000 (16:46 -0400)]
Base infrastructure to enable per-module debug messages.

I've introduced CONFIG_DYNAMIC_PRINTK_DEBUG, which when enabled centralizes
control of debugging statements on a per-module basis in one /proc file,
currently, <debugfs>/dynamic_printk/modules. When, CONFIG_DYNAMIC_PRINTK_DEBUG,
is not set, debugging statements can still be enabled as before, often by
defining 'DEBUG' for the proper compilation unit. Thus, this patch set has no
affect when CONFIG_DYNAMIC_PRINTK_DEBUG is not set.

The infrastructure currently ties into all pr_debug() and dev_dbg() calls. That
is, if CONFIG_DYNAMIC_PRINTK_DEBUG is set, all pr_debug() and dev_dbg() calls
can be dynamically enabled/disabled on a per-module basis.

Future plans include extending this functionality to subsystems, that define
their own debug levels and flags.

Usage:

Dynamic debugging is controlled by the debugfs file,
<debugfs>/dynamic_printk/modules. This file contains a list of the modules that
can be enabled. The format of the file is as follows:

<module_name> <enabled=0/1>
.
.
.

<module_name> : Name of the module in which the debug call resides
<enabled=0/1> : whether the messages are enabled or not

For example:

snd_hda_intel enabled=0
fixup enabled=1
driver enabled=0

Enable a module:

$echo "set enabled=1 <module_name>" > dynamic_printk/modules

Disable a module:

$echo "set enabled=0 <module_name>" > dynamic_printk/modules

Enable all modules:

$echo "set enabled=1 all" > dynamic_printk/modules

Disable all modules:

$echo "set enabled=0 all" > dynamic_printk/modules

Finally, passing "dynamic_printk" at the command line enables
debugging for all modules. This mode can be turned off via the above
disable command.

[gkh: minor cleanups and tweaks to make the build work quietly]

Signed-off-by: Jason Baron <jbaron@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

14 files changed:
Documentation/kernel-parameters.txt
include/asm-generic/vmlinux.lds.h
include/linux/device.h
include/linux/dynamic_printk.h [new file with mode: 0644]
include/linux/kernel.h
include/linux/module.h
kernel/module.c
lib/Kconfig.debug
lib/Makefile
lib/dynamic_printk.c [new file with mode: 0644]
net/netfilter/nf_conntrack_pptp.c
scripts/Makefile.lib
scripts/basic/Makefile
scripts/basic/hash.c [new file with mode: 0644]

index 2443f5b..b429c84 100644 (file)
@@ -1713,6 +1713,11 @@ and is between 256 and 4096 characters. It is defined in the file
                        autoconfiguration.
                        Ranges are in pairs (memory base and size).
 
+       dynamic_printk
+                       Enables pr_debug()/dev_dbg() calls if
+                       CONFIG_DYNAMIC_PRINTK_DEBUG has been enabled. These can also
+                       be switched on/off via <debugfs>/dynamic_printk/modules
+
        print-fatal-signals=
                        [KNL] debug: print fatal signals
                        print-fatal-signals=1: print segfault info to
index 7440a0d..74c5faf 100644 (file)
        CPU_DISCARD(init.data)                                          \
        CPU_DISCARD(init.rodata)                                        \
        MEM_DISCARD(init.data)                                          \
-       MEM_DISCARD(init.rodata)
+       MEM_DISCARD(init.rodata)                                        \
+       /* implement dynamic printk debug */                            \
+       VMLINUX_SYMBOL(__start___verbose_strings) = .;                  \
+       *(__verbose_strings)                                            \
+       VMLINUX_SYMBOL(__stop___verbose_strings) = .;                   \
+       . = ALIGN(8);                                                   \
+       VMLINUX_SYMBOL(__start___verbose) = .;                          \
+       *(__verbose)                                                    \
+       VMLINUX_SYMBOL(__stop___verbose) = .;
 
 #define INIT_TEXT                                                      \
        *(.init.text)                                                   \
index 60f6456..fb03446 100644 (file)
@@ -550,7 +550,11 @@ extern const char *dev_driver_string(const struct device *dev);
 #define dev_info(dev, format, arg...)          \
        dev_printk(KERN_INFO , dev , format , ## arg)
 
-#ifdef DEBUG
+#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
+#define dev_dbg(dev, format, ...) do { \
+       dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
+       } while (0)
+#elif defined(DEBUG)
 #define dev_dbg(dev, format, arg...)           \
        dev_printk(KERN_DEBUG , dev , format , ## arg)
 #else
diff --git a/include/linux/dynamic_printk.h b/include/linux/dynamic_printk.h
new file mode 100644 (file)
index 0000000..2d528d0
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef _DYNAMIC_PRINTK_H
+#define _DYNAMIC_PRINTK_H
+
+#define DYNAMIC_DEBUG_HASH_BITS 6
+#define DEBUG_HASH_TABLE_SIZE (1 << DYNAMIC_DEBUG_HASH_BITS)
+
+#define TYPE_BOOLEAN 1
+
+#define DYNAMIC_ENABLED_ALL 0
+#define DYNAMIC_ENABLED_NONE 1
+#define DYNAMIC_ENABLED_SOME 2
+
+extern int dynamic_enabled;
+
+/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which
+ * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They
+ * use independent hash functions, to reduce the chance of false positives.
+ */
+extern long long dynamic_printk_enabled;
+extern long long dynamic_printk_enabled2;
+
+struct mod_debug {
+       char *modname;
+       char *logical_modname;
+       char *flag_names;
+       int type;
+       int hash;
+       int hash2;
+} __attribute__((aligned(8)));
+
+int register_dynamic_debug_module(char *mod_name, int type, char *share_name,
+                                       char *flags, int hash, int hash2);
+
+#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
+extern int unregister_dynamic_debug_module(char *mod_name);
+extern int __dynamic_dbg_enabled_helper(char *modname, int type,
+                                       int value, int hash);
+
+#define __dynamic_dbg_enabled(module, type, value, level, hash)  ({         \
+       int __ret = 0;                                                       \
+       if (unlikely((dynamic_printk_enabled & (1LL << DEBUG_HASH)) &&       \
+                       (dynamic_printk_enabled2 & (1LL << DEBUG_HASH2))))   \
+                       __ret = __dynamic_dbg_enabled_helper(module, type,   \
+                                                               value, hash);\
+       __ret; })
+
+#define dynamic_pr_debug(fmt, ...) do {                                            \
+       static char mod_name[]                                              \
+       __attribute__((section("__verbose_strings")))                       \
+        = KBUILD_MODNAME;                                                  \
+       static struct mod_debug descriptor                                  \
+       __used                                                              \
+       __attribute__((section("__verbose"), aligned(8))) =                 \
+       { mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\
+       if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN,             \
+                                               0, 0, DEBUG_HASH))          \
+               printk(KERN_DEBUG KBUILD_MODNAME ":" fmt,                   \
+                               ##__VA_ARGS__);                             \
+       } while (0)
+
+#define dynamic_dev_dbg(dev, format, ...) do {                             \
+       static char mod_name[]                                              \
+       __attribute__((section("__verbose_strings")))                       \
+        = KBUILD_MODNAME;                                                  \
+       static struct mod_debug descriptor                                  \
+       __used                                                              \
+       __attribute__((section("__verbose"), aligned(8))) =                 \
+       { mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\
+       if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN,             \
+                                               0, 0, DEBUG_HASH))          \
+                       dev_printk(KERN_DEBUG, dev,                         \
+                                       KBUILD_MODNAME ": " format,         \
+                                       ##__VA_ARGS__);                     \
+       } while (0)
+
+#else
+
+static inline int unregister_dynamic_debug_module(const char *mod_name)
+{
+       return 0;
+}
+static inline int __dynamic_dbg_enabled_helper(char *modname, int type,
+                                               int value, int hash)
+{
+       return 0;
+}
+
+#define __dynamic_dbg_enabled(module, type, value, level, hash)  ({ 0; })
+#define dynamic_pr_debug(fmt, ...)  do { } while (0)
+#define dynamic_dev_dbg(dev, format, ...)  do { } while (0)
+#endif
+
+#endif
index 75d81f1..ededb6e 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/log2.h>
 #include <linux/typecheck.h>
 #include <linux/ratelimit.h>
+#include <linux/dynamic_printk.h>
 #include <asm/byteorder.h>
 #include <asm/bug.h>
 
@@ -303,8 +304,12 @@ static inline char *pack_hex_byte(char *buf, u8 byte)
 #define pr_info(fmt, arg...) \
        printk(KERN_INFO fmt, ##arg)
 
-#ifdef DEBUG
 /* If you are writing a driver, please use dev_dbg instead */
+#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
+#define pr_debug(fmt, ...) do { \
+       dynamic_pr_debug(fmt, ##__VA_ARGS__); \
+       } while (0)
+#elif defined(DEBUG)
 #define pr_debug(fmt, arg...) \
        printk(KERN_DEBUG fmt, ##arg)
 #else
index 68e0955..a41555c 100644 (file)
@@ -345,7 +345,6 @@ struct module
        /* Reference counts */
        struct module_ref ref[NR_CPUS];
 #endif
-
 };
 #ifndef MODULE_ARCH_INIT
 #define MODULE_ARCH_INIT {}
index d5fcd24..c527006 100644 (file)
@@ -784,6 +784,7 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
        mutex_lock(&module_mutex);
        /* Store the name of the last unloaded module for diagnostic purposes */
        strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
+       unregister_dynamic_debug_module(mod->name);
        free_module(mod);
 
  out:
@@ -1783,6 +1784,33 @@ static inline void add_kallsyms(struct module *mod,
 }
 #endif /* CONFIG_KALLSYMS */
 
+#ifdef CONFIG_DYNAMIC_PRINTK_DEBUG
+static void dynamic_printk_setup(Elf_Shdr *sechdrs, unsigned int verboseindex)
+{
+       struct mod_debug *debug_info;
+       unsigned long pos, end;
+       unsigned int num_verbose;
+
+       pos = sechdrs[verboseindex].sh_addr;
+       num_verbose = sechdrs[verboseindex].sh_size /
+                               sizeof(struct mod_debug);
+       end = pos + (num_verbose * sizeof(struct mod_debug));
+
+       for (; pos < end; pos += sizeof(struct mod_debug)) {
+               debug_info = (struct mod_debug *)pos;
+               register_dynamic_debug_module(debug_info->modname,
+                       debug_info->type, debug_info->logical_modname,
+                       debug_info->flag_names, debug_info->hash,
+                       debug_info->hash2);
+       }
+}
+#else
+static inline void dynamic_printk_setup(Elf_Shdr *sechdrs,
+                                       unsigned int verboseindex)
+{
+}
+#endif /* CONFIG_DYNAMIC_PRINTK_DEBUG */
+
 static void *module_alloc_update_bounds(unsigned long size)
 {
        void *ret = module_alloc(size);
@@ -1831,6 +1859,7 @@ static noinline struct module *load_module(void __user *umod,
 #endif
        unsigned int markersindex;
        unsigned int markersstringsindex;
+       unsigned int verboseindex;
        struct module *mod;
        long err = 0;
        void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
@@ -2117,6 +2146,7 @@ static noinline struct module *load_module(void __user *umod,
        markersindex = find_sec(hdr, sechdrs, secstrings, "__markers");
        markersstringsindex = find_sec(hdr, sechdrs, secstrings,
                                        "__markers_strings");
+       verboseindex = find_sec(hdr, sechdrs, secstrings, "__verbose");
 
        /* Now do relocations. */
        for (i = 1; i < hdr->e_shnum; i++) {
@@ -2167,6 +2197,7 @@ static noinline struct module *load_module(void __user *umod,
                marker_update_probe_range(mod->markers,
                        mod->markers + mod->num_markers);
 #endif
+       dynamic_printk_setup(sechdrs, verboseindex);
        err = module_finalize(hdr, sechdrs, mod);
        if (err < 0)
                goto cleanup;
index aa81d28..31d784d 100644 (file)
@@ -807,6 +807,61 @@ menuconfig BUILD_DOCSRC
 
          Say N if you are unsure.
 
+config DYNAMIC_PRINTK_DEBUG
+       bool "Enable dynamic printk() call support"
+       default n
+       depends on PRINTK
+       select PRINTK_DEBUG
+       help
+
+         Compiles debug level messages into the kernel, which would not
+         otherwise be available at runtime. These messages can then be
+         enabled/disabled on a per module basis. This mechanism implicitly
+         enables all pr_debug() and dev_dbg() calls. The impact of this
+         compile option is a larger kernel text size of about 2%.
+
+         Usage:
+
+         Dynamic debugging is controlled by the debugfs file,
+         dynamic_printk/modules. This file contains a list of the modules that
+         can be enabled. The format of the file is the module name, followed
+         by a set of flags that can be enabled. The first flag is always the
+         'enabled' flag. For example:
+
+               <module_name> <enabled=0/1>
+                               .
+                               .
+                               .
+
+         <module_name> : Name of the module in which the debug call resides
+         <enabled=0/1> : whether the messages are enabled or not
+
+         From a live system:
+
+               snd_hda_intel enabled=0
+               fixup enabled=0
+               driver enabled=0
+
+         Enable a module:
+
+               $echo "set enabled=1 <module_name>" > dynamic_printk/modules
+
+         Disable a module:
+
+               $echo "set enabled=0 <module_name>" > dynamic_printk/modules
+
+         Enable all modules:
+
+               $echo "set enabled=1 all" > dynamic_printk/modules
+
+         Disable all modules:
+
+               $echo "set enabled=0 all" > dynamic_printk/modules
+
+         Finally, passing "dynamic_printk" at the command line enables
+         debugging for all modules. This mode can be turned off via the above
+         disable command.
+
 source "samples/Kconfig"
 
 source "lib/Kconfig.kgdb"
index 44001af..16feaab 100644 (file)
@@ -81,6 +81,8 @@ obj-$(CONFIG_HAVE_LMB) += lmb.o
 
 obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
 
+obj-$(CONFIG_DYNAMIC_PRINTK_DEBUG) += dynamic_printk.o
+
 hostprogs-y    := gen_crc32table
 clean-files    := crc32table.h
 
diff --git a/lib/dynamic_printk.c b/lib/dynamic_printk.c
new file mode 100644 (file)
index 0000000..d640f87
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * lib/dynamic_printk.c
+ *
+ * make pr_debug()/dev_dbg() calls runtime configurable based upon their
+ * their source module.
+ *
+ * Copyright (C) 2008 Red Hat, Inc., Jason Baron <jbaron@redhat.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+
+extern struct mod_debug __start___verbose[];
+extern struct mod_debug __stop___verbose[];
+
+struct debug_name {
+       struct hlist_node hlist;
+       struct hlist_node hlist2;
+       int hash1;
+       int hash2;
+       char *name;
+       int enable;
+       int type;
+};
+
+static int nr_entries;
+static int num_enabled;
+int dynamic_enabled = DYNAMIC_ENABLED_NONE;
+static struct hlist_head module_table[DEBUG_HASH_TABLE_SIZE] =
+       { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT };
+static struct hlist_head module_table2[DEBUG_HASH_TABLE_SIZE] =
+       { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT };
+static DECLARE_MUTEX(debug_list_mutex);
+
+/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which
+ * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They
+ * use independent hash functions, to reduce the chance of false positives.
+ */
+long long dynamic_printk_enabled;
+EXPORT_SYMBOL_GPL(dynamic_printk_enabled);
+long long dynamic_printk_enabled2;
+EXPORT_SYMBOL_GPL(dynamic_printk_enabled2);
+
+/* returns the debug module pointer. */
+static struct debug_name *find_debug_module(char *module_name)
+{
+       int i;
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct debug_name *element;
+
+       element = NULL;
+       for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) {
+               head = &module_table[i];
+               hlist_for_each_entry_rcu(element, node, head, hlist)
+                       if (!strcmp(element->name, module_name))
+                               return element;
+       }
+       return NULL;
+}
+
+/* returns the debug module pointer. */
+static struct debug_name *find_debug_module_hash(char *module_name, int hash)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct debug_name *element;
+
+       element = NULL;
+       head = &module_table[hash];
+       hlist_for_each_entry_rcu(element, node, head, hlist)
+               if (!strcmp(element->name, module_name))
+                       return element;
+       return NULL;
+}
+
+/* caller must hold mutex*/
+static int __add_debug_module(char *mod_name, int hash, int hash2)
+{
+       struct debug_name *new;
+       char *module_name;
+       int ret = 0;
+
+       if (find_debug_module(mod_name)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       module_name = kmalloc(strlen(mod_name) + 1, GFP_KERNEL);
+       if (!module_name) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       module_name = strcpy(module_name, mod_name);
+       module_name[strlen(mod_name)] = '\0';
+       new = kzalloc(sizeof(struct debug_name), GFP_KERNEL);
+       if (!new) {
+               kfree(module_name);
+               ret = -ENOMEM;
+               goto out;
+       }
+       INIT_HLIST_NODE(&new->hlist);
+       INIT_HLIST_NODE(&new->hlist2);
+       new->name = module_name;
+       new->hash1 = hash;
+       new->hash2 = hash2;
+       hlist_add_head_rcu(&new->hlist, &module_table[hash]);
+       hlist_add_head_rcu(&new->hlist2, &module_table2[hash2]);
+       nr_entries++;
+out:
+       return ret;
+}
+
+int unregister_dynamic_debug_module(char *mod_name)
+{
+       struct debug_name *element;
+       int ret = 0;
+
+       down(&debug_list_mutex);
+       element = find_debug_module(mod_name);
+       if (!element) {
+               ret = -EINVAL;
+               goto out;
+       }
+       hlist_del_rcu(&element->hlist);
+       hlist_del_rcu(&element->hlist2);
+       synchronize_rcu();
+       kfree(element->name);
+       if (element->enable)
+               num_enabled--;
+       kfree(element);
+       nr_entries--;
+out:
+       up(&debug_list_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(unregister_dynamic_debug_module);
+
+int register_dynamic_debug_module(char *mod_name, int type, char *share_name,
+                                       char *flags, int hash, int hash2)
+{
+       struct debug_name *elem;
+       int ret = 0;
+
+       down(&debug_list_mutex);
+       elem = find_debug_module(mod_name);
+       if (!elem) {
+               if (__add_debug_module(mod_name, hash, hash2))
+                       goto out;
+               elem = find_debug_module(mod_name);
+               if (dynamic_enabled == DYNAMIC_ENABLED_ALL &&
+                               !strcmp(mod_name, share_name)) {
+                       elem->enable = true;
+                       num_enabled++;
+               }
+       }
+       elem->type |= type;
+out:
+       up(&debug_list_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(register_dynamic_debug_module);
+
+int __dynamic_dbg_enabled_helper(char *mod_name, int type, int value, int hash)
+{
+       struct debug_name *elem;
+       int ret = 0;
+
+       if (dynamic_enabled == DYNAMIC_ENABLED_ALL)
+               return 1;
+       rcu_read_lock();
+       elem = find_debug_module_hash(mod_name, hash);
+       if (elem && elem->enable)
+               ret = 1;
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__dynamic_dbg_enabled_helper);
+
+static void set_all(bool enable)
+{
+       struct debug_name *e;
+       struct hlist_node *node;
+       int i;
+       long long enable_mask;
+
+       for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) {
+               if (module_table[i].first != NULL) {
+                       hlist_for_each_entry(e, node, &module_table[i], hlist) {
+                               e->enable = enable;
+                       }
+               }
+       }
+       if (enable)
+               enable_mask = ULLONG_MAX;
+       else
+               enable_mask = 0;
+       dynamic_printk_enabled = enable_mask;
+       dynamic_printk_enabled2 = enable_mask;
+}
+
+static int disabled_hash(int i, bool first_table)
+{
+       struct debug_name *e;
+       struct hlist_node *node;
+
+       if (first_table) {
+               hlist_for_each_entry(e, node, &module_table[i], hlist) {
+                       if (e->enable)
+                               return 0;
+               }
+       } else {
+               hlist_for_each_entry(e, node, &module_table2[i], hlist2) {
+                       if (e->enable)
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+static ssize_t pr_debug_write(struct file *file, const char __user *buf,
+                               size_t length, loff_t *ppos)
+{
+       char *buffer, *s, *value_str, *setting_str;
+       int err, value;
+       struct debug_name *elem = NULL;
+       int all = 0;
+
+       if (length > PAGE_SIZE || length < 0)
+               return -EINVAL;
+
+       buffer = (char *)__get_free_page(GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       err = -EFAULT;
+       if (copy_from_user(buffer, buf, length))
+               goto out;
+
+       err = -EINVAL;
+       if (length < PAGE_SIZE)
+               buffer[length] = '\0';
+       else if (buffer[PAGE_SIZE-1])
+               goto out;
+
+       err = -EINVAL;
+       down(&debug_list_mutex);
+
+       if (strncmp("set", buffer, 3))
+               goto out_up;
+       s = buffer + 3;
+       setting_str = strsep(&s, "=");
+       if (s == NULL)
+               goto out_up;
+       setting_str = strstrip(setting_str);
+       value_str = strsep(&s, " ");
+       if (s == NULL)
+               goto out_up;
+       s = strstrip(s);
+       if (!strncmp(s, "all", 3))
+               all = 1;
+       else
+               elem = find_debug_module(s);
+       if (!strncmp(setting_str, "enable", 6)) {
+               value = !!simple_strtol(value_str, NULL, 10);
+               if (all) {
+                       if (value) {
+                               set_all(true);
+                               num_enabled = nr_entries;
+                               dynamic_enabled = DYNAMIC_ENABLED_ALL;
+                       } else {
+                               set_all(false);
+                               num_enabled = 0;
+                               dynamic_enabled = DYNAMIC_ENABLED_NONE;
+                       }
+                       err = 0;
+               } else {
+                       if (elem) {
+                               if (value && (elem->enable == 0)) {
+                                       dynamic_printk_enabled |=
+                                                       (1LL << elem->hash1);
+                                       dynamic_printk_enabled2 |=
+                                                       (1LL << elem->hash2);
+                                       elem->enable = 1;
+                                       num_enabled++;
+                                       dynamic_enabled = DYNAMIC_ENABLED_SOME;
+                                       err = 0;
+                                       printk(KERN_DEBUG
+                                              "debugging enabled for module %s",
+                                              elem->name);
+                               } else if (!value && (elem->enable == 1)) {
+                                       elem->enable = 0;
+                                       num_enabled--;
+                                       if (disabled_hash(elem->hash1, true))
+                                               dynamic_printk_enabled &=
+                                                       ~(1LL << elem->hash1);
+                                       if (disabled_hash(elem->hash2, false))
+                                               dynamic_printk_enabled2 &=
+                                                       ~(1LL << elem->hash2);
+                                       if (num_enabled)
+                                               dynamic_enabled =
+                                                       DYNAMIC_ENABLED_SOME;
+                                       else
+                                               dynamic_enabled =
+                                                       DYNAMIC_ENABLED_NONE;
+                                       err = 0;
+                                       printk(KERN_DEBUG
+                                              "debugging disabled for module "
+                                              "%s", elem->name);
+                               }
+                       }
+               }
+       }
+       if (!err)
+               err = length;
+out_up:
+       up(&debug_list_mutex);
+out:
+       free_page((unsigned long)buffer);
+       return err;
+}
+
+static void *pr_debug_seq_start(struct seq_file *f, loff_t *pos)
+{
+       return (*pos < DEBUG_HASH_TABLE_SIZE) ? pos : NULL;
+}
+
+static void *pr_debug_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       (*pos)++;
+       if (*pos >= DEBUG_HASH_TABLE_SIZE)
+               return NULL;
+       return pos;
+}
+
+static void pr_debug_seq_stop(struct seq_file *s, void *v)
+{
+       /* Nothing to do */
+}
+
+static int pr_debug_seq_show(struct seq_file *s, void *v)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct debug_name *elem;
+       unsigned int i = *(loff_t *) v;
+
+       rcu_read_lock();
+       head = &module_table[i];
+       hlist_for_each_entry_rcu(elem, node, head, hlist) {
+               seq_printf(s, "%s enabled=%d", elem->name, elem->enable);
+               seq_printf(s, "\n");
+       }
+       rcu_read_unlock();
+       return 0;
+}
+
+static struct seq_operations pr_debug_seq_ops = {
+       .start = pr_debug_seq_start,
+       .next  = pr_debug_seq_next,
+       .stop  = pr_debug_seq_stop,
+       .show  = pr_debug_seq_show
+};
+
+static int pr_debug_open(struct inode *inode, struct file *filp)
+{
+       return seq_open(filp, &pr_debug_seq_ops);
+}
+
+static const struct file_operations pr_debug_operations = {
+       .open           = pr_debug_open,
+       .read           = seq_read,
+       .write          = pr_debug_write,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+static int __init dynamic_printk_init(void)
+{
+       struct dentry *dir, *file;
+       struct mod_debug *iter;
+       unsigned long value;
+
+       dir = debugfs_create_dir("dynamic_printk", NULL);
+       if (!dir)
+               return -ENOMEM;
+       file = debugfs_create_file("modules", 0644, dir, NULL,
+                                       &pr_debug_operations);
+       if (!file) {
+               debugfs_remove(dir);
+               return -ENOMEM;
+       }
+       for (value = (unsigned long)__start___verbose;
+               value < (unsigned long)__stop___verbose;
+               value += sizeof(struct mod_debug)) {
+                       iter = (struct mod_debug *)value;
+                       register_dynamic_debug_module(iter->modname,
+                               iter->type,
+                               iter->logical_modname,
+                               iter->flag_names, iter->hash, iter->hash2);
+       }
+       return 0;
+}
+module_init(dynamic_printk_init);
+/* may want to move this earlier so we can get traces as early as possible */
+
+static int __init dynamic_printk_setup(char *str)
+{
+       if (str)
+               return -ENOENT;
+       set_all(true);
+       return 0;
+}
+/* Use early_param(), so we can get debug output as early as possible */
+early_param("dynamic_printk", dynamic_printk_setup);
index 373e51e..1bc3001 100644 (file)
@@ -65,7 +65,7 @@ void
                             struct nf_conntrack_expect *exp) __read_mostly;
 EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn);
 
-#ifdef DEBUG
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
 /* PptpControlMessageType names */
 const char *const pptp_msg_name[] = {
        "UNKNOWN_MESSAGE",
index ea48b82..b4ca38a 100644 (file)
@@ -96,6 +96,14 @@ basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(basetarget)))"
 modname_flags  = $(if $(filter 1,$(words $(modname))),\
                  -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))")
 
+#hash values
+ifdef CONFIG_DYNAMIC_PRINTK_DEBUG
+debug_flags = -D"DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname))"\
+              -D"DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname))"
+else
+debug_flags =
+endif
+
 orig_c_flags   = $(KBUILD_CFLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o)
 _c_flags       = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
 _a_flags       = $(KBUILD_AFLAGS) $(asflags-y) $(AFLAGS_$(basetarget).o)
@@ -121,7 +129,8 @@ endif
 
 c_flags        = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \
                 $(__c_flags) $(modkern_cflags) \
-                -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags)
+                -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) \
+                 $(debug_flags)
 
 a_flags        = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \
                 $(__a_flags) $(modkern_aflags)
index 4c324a1..0955995 100644 (file)
@@ -9,7 +9,7 @@
 # fixdep:       Used to generate dependency information during build process
 # docproc:      Used in Documentation/DocBook
 
-hostprogs-y    := fixdep docproc
+hostprogs-y    := fixdep docproc hash
 always         := $(hostprogs-y)
 
 # fixdep is needed to compile other host programs
diff --git a/scripts/basic/hash.c b/scripts/basic/hash.c
new file mode 100644 (file)
index 0000000..3299ad7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc., Jason Baron <jbaron@redhat.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DYNAMIC_DEBUG_HASH_BITS 6
+
+static const char *program;
+
+static void usage(void)
+{
+       printf("Usage: %s <djb2|r5> <modname>\n", program);
+       exit(1);
+}
+
+/* djb2 hashing algorithm by Dan Bernstein. From:
+ * http://www.cse.yorku.ca/~oz/hash.html
+ */
+
+unsigned int djb2_hash(char *str)
+{
+       unsigned long hash = 5381;
+       int c;
+
+       c = *str;
+       while (c) {
+               hash = ((hash << 5) + hash) + c;
+               c = *++str;
+       }
+       return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1));
+}
+
+unsigned int r5_hash(char *str)
+{
+       unsigned long hash = 0;
+       int c;
+
+       c = *str;
+       while (c) {
+               hash = (hash + (c << 4) + (c >> 4)) * 11;
+               c = *++str;
+       }
+       return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1));
+}
+
+int main(int argc, char *argv[])
+{
+       program = argv[0];
+
+       if (argc != 3)
+               usage();
+       if (!strcmp(argv[1], "djb2"))
+               printf("%d\n", djb2_hash(argv[2]));
+       else if (!strcmp(argv[1], "r5"))
+               printf("%d\n", r5_hash(argv[2]));
+       else
+               usage();
+       exit(0);
+}
+