lkml.org 
[lkml]   [2009]   [Jan]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[patch 4/5] Add the dprintk module to allow dprintks to be activated/deactivated singly
Add a module which provides a /proc/dprintk file.  Reading the file
lists all the dprintks. You can write simple queries to the file to
describe a set of dprintks and what flags to enable or disable. There
are three flags, which will make the dprintk() print the message,
or dump the kernel stack, or take a crash dump.

Signed-off-by: Greg Banks <gnb@sgi.com>
---
Documentation/dprintk-howto.txt | 267 +++++++++
init/Kconfig | 15
kernel/Makefile | 1
kernel/dprintk.c | 848 +++++++++++++++++++++++++++++
4 files changed, 1131 insertions(+)
Index: bfields/Documentation/dprintk-howto.txt
===================================================================
--- /dev/null
+++ bfields/Documentation/dprintk-howto.txt
@@ -0,0 +1,267 @@
+
+Introduction
+============
+
+This document describes how to use the SGI dprintk module.
+
+Dprintk is a module designed to allow you to control the behaviour of
+dprintk() debugging print statements located in the source of modules
+which have been compiled against the dprintk module.
+
+The NFS client, NFS server, NLM code, and sunrpc code all already
+contain many such dprintk()s (nearly six hundred at last count)
+in strategic locations. Traditionally those dprintk()s have been
+controlled using the four bitmasks which can be written to thus:
+
+echo 65535 > /proc/sys/sunrpc/nfs_debug
+echo 65535 > /proc/sys/sunrpc/nfsd_debug
+echo 65535 > /proc/sys/sunrpc/nlm_debug
+echo 65535 > /proc/sys/sunrpc/rpc_debug
+
+However this technique is something of a blunt instrument. Typically
+each bit will control several, possibly dozens, of dprintks(),
+so enabling just the debugging information you need is dificult.
+Enter the dprintk module, which allows turning on or off any individual
+dprintk() or group of dprintk()s.
+
+The dprintk module has even more useful features:
+
+ * Simple query language allows turning on and off dprintks by matching
+ any combination of:
+
+ - source filename
+ - function name
+ - line number (including ranges of line numbers)
+ - module name
+ - format string
+
+ * Provides a /proc/dprintk which can be read to display the complete
+ list of all dprintk()s known, to help guide you
+
+ * The module is optional. The NFS dprintk()s still work with the
+ /proc/sys/sunrpc/ bitmasks. The dprintk module can be loaded or
+ unloaded at any time.
+
+ * In addition to enabling the print, two other behaviours can be enabled:
+
+ - printing a kernel stack trace
+ - crashing the kernel, so that a dump can be taken
+
+Controlling dprintk() Behaviour
+===============================
+
+You can control the behaviour of dprintk()s by writing a command
+to /proc/dprintk, e.g. using the echo command:
+
+nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk
+
+If you make a mistake with the syntax, the write will fail thus:
+
+nullarbor:~ # echo 'file svcsock.c wtf 1 +p' > /proc/dprintk
+-bash: echo: write error: Invalid argument
+
+Viewing dprintk() Behaviour
+===========================
+
+You can view the currently configured behaviour of all the dprintk()s
+in loaded modules by reading /proc/dprintk. For example:
+
+nullarbor:~ # cat /proc/dprintk
+# filename:lineno [module]function flags format
+/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup - "SVCRDMA\040Module\040Removed,\040deregister\040RPC\040RDMA\040transport\012"
+/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init - "\011max_inline\040\040\040\040\040\040\040:\040%d\012"
+/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init - "\011sq_depth\040\040\040\040\040\040\040\040\040:\040%d\012"
+/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init - "\011max_requests\040\040\040\040\040:\040%d\012"
+...
+
+
+You can also apply standard Unix text manipulation filters to this
+data, e.g.
+
+nullarbor:~ # grep -i rdma /proc/dprintk | wc -l
+62
+
+nullarbor:~ # grep -i tcp /proc/dprintk | wc -l
+42
+
+Note in particular that the third column shows the enabled behaviour
+flags for each dprintk() callsite (see below for definitions of the
+flags). The default value, no extra behaviour enabled, is "-". So
+you can view all the dprintk() callsites with any non-default flags:
+
+nullarbor:~ # awk '$3 != "-"' /proc/dprintk
+# filename:lineno [module]function flags format
+/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p "svc_process:\040st_sendto\040returned\040%d\012"
+
+
+Command Language Reference
+==========================
+
+At the lexical level, a command comprises a sequence of words separated
+by whitespace characters. Note that newlines are treated as word
+separators and do *not* end a command or allow multiple commands to
+be done together. So these are all equivalent:
+
+nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' > /proc/dprintk
+nullarbor:~ # echo -c ' file svcsock.c line 1603 +p ' > /proc/dprintk
+nullarbor:~ # echo -c 'file svcsock.c\nline 1603 +p' > /proc/dprintk
+nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > /proc/dprintk
+
+Commands are bounded by a write() system call. If you want to do
+multiple commands you need to do a separate "echo" for each, like:
+
+nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk ;\
+> echo 'file svcsock.c line 1563 +p' > /proc/dprintk
+
+or even like:
+
+nullarbor:~ # (
+> echo 'file svcsock.c line 1603 +p' ;\
+> echo 'file svcsock.c line 1563 +p' ;\
+> ) > /proc/dprintk
+
+At the syntactical level, a command comprises a sequence of match
+specifications, followed by a flags change specification.
+
+command ::= match-spec* flags-spec
+
+The match-spec's are used to choose a subset of the known dprintk()
+callsites to which to apply the flags-spec. Think of them as a query
+with implicit ANDs between each pair. Note that an empty list of
+match-specs is possible, but is not very useful because it will not
+match any dprintk() callsites.
+
+A match specification comprises a keyword, which controls the attribute
+of the callsite to be compared, and a value to compare against. Possible
+keywords are:
+
+match-spec ::= 'func' string |
+ 'file' string |
+ 'module' string |
+ 'format' string |
+ 'line' line-range
+
+line-range ::= lineno |
+ '-'lineno |
+ lineno'-' |
+ lineno'-'lineno
+// Note: line-range cannot contain space, e.g.
+// "1-30" is valid range but "1 - 30" is not.
+
+lineno ::= unsigned-int
+
+The meanings of each keyword are:
+
+func
+ The given string is compared against the function name
+ of each callsite. Example:
+
+ func svc_tcp_accept
+
+file
+ The given string is compared against either the full
+ pathname or the basename of the source file of each
+ callsite. Examples:
+
+ file svcsock.c
+ file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c
+
+module
+ The given string is compared against the module name
+ of each callsite. The module name is the string as
+ seen in "lsmod", i.e. without the directory or the .ko
+ suffix and with '-' changed to '_'. Examples:
+
+ module sunrpc
+ module nfsd
+
+format
+ The given string is searched for in the dprintk() format
+ string. Note that the string does not need to match the
+ entire format, only some part. Whitespace and other
+ special characters can be escaped using C octal character
+ escape \ooo notation, e.g. the space character is \040.
+ Examples:
+
+ format svcrdma: // many of the NFS/RDMA server dprintks
+ format readahead // some dprintks in the readahead cache
+ format nfsd:\040SETATTR // how to match a format with whitespace
+
+line
+ The given line number or range of line numbers is compared
+ against the line number of each dprintk() callsite. A single
+ line number matches the callsite line number exactly. A
+ range of line numbers matches any callsite between the first
+ and last line number inclusive. An empty first number means
+ the first line in the file, an empty line number means the
+ last number in the file. Examples:
+
+ line 1603 // exactly line 1603
+ line 1600-1605 // the six lines from line 1600 to line 1605
+ line -1605 // the 1605 lines from line 1 to line 1605
+ line 1600- // all lines from line 1600 to the end of the file
+
+The flags specification comprises a change operation followed
+by one or more flag characters. The change operation is one
+of the characters:
+
+-
+ remove the given flags
+
++
+ add the given flags
+
+=
+ set the flags to the given flags
+
+The flags are:
+
+p
+ Causes a printk() message to be emitted to dmesg,
+ i.e. the obvious semantic of a dprintk().
+
+s
+ Causes a kernel stack trace to be emitted to dmesg.
+ The printk() is emitted first, even if the 'p' flag
+ is not specified.
+
+c
+ Causes the kernel to panic using the kernel BUG()
+ macro. This will cause the machine to drop into KDB
+ or take a kernel crash dump, according to how the
+ machine has been configured. The printk() is emitted
+ first, even if the 'p' flag is not specified.
+
+Note the regexp ^[-+=][scp]+$ matches a flags specification.
+Note also that there is no convenient syntax to remove all
+the flags at once, you need to use "-psc".
+
+Examples
+========
+
+// enable the message at line 1603 of file svcsock.c
+nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > /proc/dprintk
+
+// enable all the messages in file svcsock.c
+nullarbor:~ # echo -n 'file svcsock.c +p' > /proc/dprintk
+
+// enable all the messages in file svcsock.c
+nullarbor:~ # echo -n 'file svcsock.c +p' > /proc/dprintk
+
+// enable all the messages in the NFS server module
+nullarbor:~ # echo -n 'module nfsd +p' > /proc/dprintk
+
+// enable all 12 messages in the function svc_process()
+nullarbor:~ # echo -n 'func svc_process +p' > /proc/dprintk
+
+// disable all 12 messages in the function svc_process()
+nullarbor:~ # echo -n 'func svc_process -p' > /proc/dprintk
+
+// print a stack trace on every upcall to rpc.mountd or rpc.idmapd
+nullarbor:~ # echo -n 'format Want\040update,\040refage +s' > /proc/dprintk
+
+// cause a kernel crash dump when an RPC call to an
+// unknown RPC program number is received
+nullarbor:~ # echo -n 'format unknown\040program +c' > /proc/dprintk
+
+
Index: bfields/kernel/dprintk.c
===================================================================
--- /dev/null
+++ bfields/kernel/dprintk.c
@@ -0,0 +1,848 @@
+/*
+ * dprintk - a generic /proc interface for enabling individual debug printks.
+ *
+ * By Greg Banks <gnb@melbourne.sgi.com>
+ * Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved.
+ * $Id: dprintk.c,v 1.1 2008/09/04 04:46:06 gnb Exp $
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kallsyms.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/list.h>
+#include <linux/sysctl.h>
+#include <linux/ctype.h>
+#include <asm/uaccess.h>
+#include <linux/dprintk.h>
+
+struct dprintk_table
+{
+ struct list_head link;
+ struct module *module;
+ unsigned int num_dprintks;
+ struct _dprintk *dprintks;
+};
+
+struct dprintk_query
+{
+ const char *filename;
+ const char *module;
+ const char *function;
+ const char *format;
+ unsigned int first_lineno, last_lineno;
+};
+
+struct dprintk_iter
+{
+ struct dprintk_table *table;
+ unsigned int idx;
+};
+
+static DEFINE_MUTEX(dprintk_lock);
+static LIST_HEAD(dprintk_tables);
+static int verbose = 0;
+
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "Set this parameter to non-zero to debug "
+ "the dprintk module itself.");
+
+/* Return the last part of a pathname */
+static inline const char *basename(const char *path)
+{
+ const char *tail = strrchr(path, '/');
+ return (tail ? tail+1 : path);
+}
+
+/* format a string into buf[] which describes the _dprintk's flags */
+static char *dprintk_describe_flags(struct _dprintk *dp, char *buf,
+ size_t maxlen)
+{
+ char *p = buf;
+
+ BUG_ON(maxlen < 4);
+ if (dp->flags & _DPRINTK_FLAGS_CRASH)
+ *p++ = 'c';
+ if (dp->flags & _DPRINTK_FLAGS_STACK)
+ *p++ = 's';
+ if (dp->flags & _DPRINTK_FLAGS_PRINT)
+ *p++ = 'p';
+ if (p == buf)
+ *p++ = '-';
+ *p = '\0';
+
+ return buf;
+}
+
+/*
+ * Search the tables for _dprintk's which match the given
+ * `query' and apply the `flags' and `mask' to them. Tells
+ * the user which dprintk's were changed, or whether none
+ * were matched.
+ */
+static void dprintk_change(const struct dprintk_query *query,
+ unsigned int flags, unsigned int mask)
+{
+ int i;
+ struct dprintk_table *dt;
+ unsigned int newflags;
+ unsigned int nfound = 0;
+ char flagbuf[8];
+
+ /* search for matching dprintks */
+ mutex_lock(&dprintk_lock);
+ list_for_each_entry(dt, &dprintk_tables, link) {
+
+ /* match against the module name */
+ if (query->module != NULL &&
+ strcmp(query->module, dt->module->name))
+ continue;
+
+ for (i = 0 ; i < dt->num_dprintks ; i++) {
+ struct _dprintk *dp = &dt->dprintks[i];
+
+ /* match against the source filename */
+ if (query->filename != NULL &&
+ strcmp(query->filename, dp->filename) &&
+ strcmp(query->filename, basename(dp->filename)))
+ continue;
+
+ /* match against the function */
+ if (query->function != NULL &&
+ strcmp(query->function, dp->function))
+ continue;
+
+ /* match against the format */
+ if (query->format != NULL &&
+ strstr(dp->format, query->format) == NULL)
+ continue;
+
+ /* match against the line number range */
+ if (query->first_lineno &&
+ dp->lineno < query->first_lineno)
+ continue;
+ if (query->last_lineno &&
+ dp->lineno > query->last_lineno)
+ continue;
+
+ nfound++;
+
+ newflags = (dp->flags & mask) | flags;
+ if (newflags == dp->flags)
+ continue;
+ dp->flags = newflags;
+
+ printk(KERN_INFO "dprintk: changed %s:%d [%s]%s %s\n",
+ dp->filename, dp->lineno,
+ dt->module->name, dp->function,
+ dprintk_describe_flags(dp, flagbuf, sizeof(flagbuf)));
+ }
+ }
+ mutex_unlock(&dprintk_lock);
+
+ if (!nfound)
+ printk(KERN_INFO "dprintk: no matches for query\n");
+}
+
+/*
+ * Wrapper around strsep() to collapse the multiple empty tokens
+ * that it returns when fed sequences of separator characters.
+ * Now, if we had strtok_r()...
+ */
+static inline char *nearly_strtok_r(char **p, const char *sep)
+{
+ char *r;
+
+ while ((r = strsep(p, sep)) != NULL && *r == '\0')
+ ;
+ return r;
+}
+
+/*
+ * Split the buffer `buf' into space-separated words.
+ * Return the number of such words or <0 on error.
+ */
+static int dprintk_tokenize(char *buf, char *words[], int maxwords)
+{
+ int nwords = 0;
+
+ while (nwords < maxwords &&
+ (words[nwords] = nearly_strtok_r(&buf, " \t\r\n")) != NULL)
+ nwords++;
+ if (buf)
+ return -EINVAL; /* ran out of words[] before bytes */
+
+ if (verbose) {
+ int i;
+ printk(KERN_INFO "%s: split into words:", __FUNCTION__);
+ for (i = 0 ; i < nwords ; i++)
+ printk(" \"%s\"", words[i]);
+ printk("\n");
+ }
+
+ return nwords;
+}
+
+/*
+ * Parse a single line number. Note that the empty string ""
+ * is treated as a special case and converted to zero, which
+ * is later treated as a "don't care" value.
+ */
+static inline int parse_lineno(const char *str, unsigned int *val)
+{
+ char *end = NULL;
+ BUG_ON(str == NULL);
+ if (*str == '\0') {
+ *val = 0;
+ return 0;
+ }
+ *val = simple_strtoul(str, &end, 10);
+ return (end == NULL || end == str || *end != '\0' ? -EINVAL : 0);
+}
+
+/*
+ * Undo octal escaping in a string, inplace. This is useful to
+ * allow the user to express a query which matches a format
+ * containing embedded spaces.
+ */
+#define isodigit(c) ((c) >= '0' && (c) <= '7')
+static char *unescape(char *str)
+{
+ char *in = str;
+ char *out = str;
+
+ while (*in) {
+ if (*in == '\\') {
+ if (in[1] == '\\') {
+ *out++ = '\\';
+ in += 2;
+ continue;
+ } else if (in[1] == 't') {
+ *out++ = '\t';
+ in += 2;
+ continue;
+ } else if (in[1] == 'n') {
+ *out++ = '\n';
+ in += 2;
+ continue;
+ } else if (isodigit(in[1]) &&
+ isodigit(in[2]) &&
+ isodigit(in[3])) {
+ *out++ = ((in[1] - '0')<<6) |
+ ((in[2] - '0')<<3) |
+ (in[3] - '0');
+ in += 4;
+ continue;
+ }
+ }
+ *out++ = *in++;
+ }
+ *out = '\0';
+
+ return str;
+}
+
+/*
+ * Parse words[] as a dprintk query specification, which is a series
+ * of (keyword, value) pairs chosen from these possibilities:
+ *
+ * func <function-name>
+ * file <full-pathname>
+ * file <base-filename>
+ * module <module-name>
+ * format <escaped-string-to-find-in-format>
+ * line <lineno>
+ * line <first-lineno>-<last-lineno> // where either may be empty
+ */
+static int dprintk_parse_query(char *words[], int nwords,
+ struct dprintk_query *query)
+{
+ unsigned int i;
+
+ /* check we have an even number of words */
+ if (nwords % 2 != 0)
+ return -EINVAL;
+ memset(query, 0, sizeof(*query));
+
+ for (i = 0 ; i < nwords ; i += 2) {
+ if (!strcmp(words[i], "func"))
+ query->function = words[i+1];
+ else if (!strcmp(words[i], "file"))
+ query->filename = words[i+1];
+ else if (!strcmp(words[i], "module"))
+ query->module = words[i+1];
+ else if (!strcmp(words[i], "format"))
+ query->format = unescape(words[i+1]);
+ else if (!strcmp(words[i], "line")) {
+ char *first = words[i+1];
+ char *last = strchr(first, '-');
+ if (last)
+ *last++ = '\0';
+ if (parse_lineno(first, &query->first_lineno) < 0)
+ return -EINVAL;
+ if (last != NULL) {
+ /* range <first>-<last> */
+ if (parse_lineno(last, &query->last_lineno) < 0)
+ return -EINVAL;
+ } else {
+ query->last_lineno = query->first_lineno;
+ }
+ } else {
+ if (verbose)
+ printk(KERN_ERR "%s: unknown keyword \"%s\"\n",
+ __FUNCTION__, words[i]);
+ return -EINVAL;
+ }
+ }
+
+ if (verbose)
+ printk(KERN_INFO "%s: q->function=\"%s\" q->filename=\"%s\" "
+ "q->module=\"%s\" q->format=\"%s\" q->lineno=%u-%u\n",
+ __FUNCTION__, query->function, query->filename,
+ query->module, query->format, query->first_lineno,
+ query->last_lineno);
+
+ return 0;
+}
+
+/*
+ * Parse `str' as a flags specification, format [-+=][scp]+.
+ * Sets up *maskp and *flagsp to be used when changing the
+ * flags fields of matched _dprintk's. Returns 0 on success
+ * or <0 on error.
+ */
+static int dprintk_parse_flags(const char *str, unsigned int *flagsp,
+ unsigned int *maskp)
+{
+ unsigned flags = 0;
+ int op = '=';
+
+ switch (*str) {
+ case '+':
+ case '-':
+ case '=':
+ op = *str++;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (verbose)
+ printk(KERN_INFO "%s: op='%c'\n", __FUNCTION__, op);
+
+ for ( ; *str ; ++str) {
+ switch (*str) {
+ case 'p':
+ flags |= _DPRINTK_FLAGS_PRINT;
+ break;
+ case 's':
+ flags |= _DPRINTK_FLAGS_STACK;
+ break;
+ case 'c':
+ flags |= _DPRINTK_FLAGS_CRASH;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (flags == 0)
+ return -EINVAL;
+ if (verbose)
+ printk(KERN_INFO "%s: flags=0x%x\n", __FUNCTION__, flags);
+
+ /* calculate final *flagsp, *maskp according to mask and op */
+ switch (op) {
+ case '=':
+ *maskp = 0;
+ *flagsp = flags;
+ break;
+ case '+':
+ *maskp = ~0U;
+ *flagsp = flags;
+ break;
+ case '-':
+ *maskp = ~flags;
+ *flagsp = 0;
+ break;
+ }
+ if (verbose)
+ printk(KERN_INFO "%s: *flagsp=0x%x *maskp=0x%x\n",
+ __FUNCTION__, *flagsp, *maskp);
+ return 0;
+}
+
+/*
+ * File_ops->write method for /proc/dprintk. Gathers the
+ * command text from userspace, parses and executes it.
+ */
+static ssize_t dprintk_proc_write(struct file *file, const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ unsigned int flags = 0, mask = 0;
+ struct dprintk_query query;
+#define MAXWORDS 9
+ int nwords;
+ char *words[MAXWORDS];
+ char tmpbuf[256];
+
+ if (len == 0)
+ return 0;
+ /* we don't check *offp -- multiple writes() are allowed */
+ if (len > sizeof(tmpbuf)-1)
+ return -E2BIG;
+ if (copy_from_user(tmpbuf, ubuf, len))
+ return -EFAULT;
+ tmpbuf[len] = '\0';
+ if (verbose)
+ printk(KERN_INFO "%s: read %d bytes from userspace\n",
+ __FUNCTION__, (int)len);
+
+ nwords = dprintk_tokenize(tmpbuf, words, MAXWORDS);
+ if (nwords < 0)
+ return -EINVAL;
+ if (dprintk_parse_query(words, nwords-1, &query))
+ return -EINVAL;
+ if (dprintk_parse_flags(words[nwords-1], &flags, &mask))
+ return -EINVAL;
+
+ /* actually go and implement the change */
+ dprintk_change(&query, flags, mask);
+
+ *offp += len;
+ return len;
+}
+
+/*
+ * Set the iterator to point to the first _dprintk object
+ * and return a pointer to that first object. Returns
+ * NULL if there are no _dprintks at all.
+ */
+static struct _dprintk *dprintk_iter_first(struct dprintk_iter *iter)
+{
+ if (list_empty(&dprintk_tables)) {
+ iter->table = NULL;
+ iter->idx = 0;
+ return NULL;
+ }
+ iter->table = list_entry(dprintk_tables.next,
+ struct dprintk_table, link);
+ iter->idx = 0;
+ return &iter->table->dprintks[iter->idx];
+}
+
+/*
+ * Advance the iterator to point to the next _dprintk
+ * object from the one the iterator currently points at,
+ * and returns a pointer to the new _dprintk. Returns
+ * NULL if the iterator has seen all the _dprintks.
+ */
+static struct _dprintk *dprintk_iter_next(struct dprintk_iter *iter)
+{
+ if (iter->table == NULL)
+ return NULL;
+ if (++iter->idx == iter->table->num_dprintks) {
+ /* iterate to next table */
+ iter->idx = 0;
+ if (list_is_last(&iter->table->link, &dprintk_tables)) {
+ iter->table = NULL;
+ return NULL;
+ }
+ iter->table = list_entry(iter->table->link.next,
+ struct dprintk_table, link);
+ }
+ return &iter->table->dprintks[iter->idx];
+}
+
+/*
+ * Seq_ops start method. Called at the start of every
+ * read() call from userspace. Takes the dprintk_lock and
+ * seeks the seq_file's iterator to the given position.
+ */
+static void *dprintk_proc_start(struct seq_file *m, loff_t *pos)
+{
+ struct dprintk_iter *iter = m->private;
+ struct _dprintk *dp;
+ int n = *pos;
+
+ if (verbose)
+ printk(KERN_INFO "%s: called m=%p *pos=%lld\n",
+ __FUNCTION__, m, (unsigned long long)*pos);
+
+ mutex_lock(&dprintk_lock);
+
+ if (!n)
+ return SEQ_START_TOKEN;
+ if (n < 0)
+ return NULL;
+ dp = dprintk_iter_first(iter);
+ while (dp != NULL && --n > 0)
+ dp = dprintk_iter_next(iter);
+ return dp;
+}
+
+/*
+ * Seq_ops next method. Called several times within a read()
+ * call from userspace, with dprintk_lock held. Walks to the
+ * next _dprintk object with a special case for the header line.
+ */
+static void *dprintk_proc_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ struct dprintk_iter *iter = m->private;
+ struct _dprintk *dp;
+
+ if (verbose)
+ printk(KERN_INFO "%s: called m=%p p=%p *pos=%lld\n",
+ __FUNCTION__, m, p, (unsigned long long)*pos);
+
+ if (p == SEQ_START_TOKEN)
+ dp = dprintk_iter_first(iter);
+ else
+ dp = dprintk_iter_next(iter);
+ ++*pos;
+ return dp;
+}
+
+/*
+ * Seq_ops show method. Called several times within a read()
+ * call from userspace, with dprintk_lock held. Formats the
+ * current _dprintk as a single human-readable line, with a
+ * special case for the header line.
+ */
+static int dprintk_proc_show(struct seq_file *m, void *p)
+{
+ struct dprintk_iter *iter = m->private;
+ struct _dprintk *dp = p;
+ char flagsbuf[8];
+
+ if (verbose)
+ printk(KERN_INFO "%s: called m=%p p=%p\n",
+ __FUNCTION__, 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 \"",
+ dp->filename, dp->lineno,
+ iter->table->module->name, dp->function,
+ dprintk_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 dprintk_lock.
+ */
+static void dprintk_proc_stop(struct seq_file *m, void *p)
+{
+ if (verbose)
+ printk(KERN_INFO "%s: called m=%p p=%p\n",
+ __FUNCTION__, m, p);
+ mutex_unlock(&dprintk_lock);
+}
+
+static struct seq_operations dprintk_proc_seqops = {
+ .start = dprintk_proc_start,
+ .next = dprintk_proc_next,
+ .show = dprintk_proc_show,
+ .stop = dprintk_proc_stop
+};
+
+/*
+ * File_ops->open method for /proc/dprintk. Does the seq_file
+ * setup dance, and also creates an iterator to walk the _dprintks.
+ * 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 dprintk_proc_open(struct inode *inode, struct file *file)
+{
+ struct dprintk_iter *iter;
+ int err;
+
+ if (verbose)
+ printk(KERN_INFO "%s: called\n", __FUNCTION__);
+
+ iter = kzalloc(sizeof(*iter), GFP_KERNEL);
+ if (iter == NULL)
+ return -ENOMEM;
+
+ err = seq_open(file, &dprintk_proc_seqops);
+ if (err) {
+ kfree(iter);
+ return err;
+ }
+ ((struct seq_file *) file->private_data)->private = iter;
+ return 0;
+}
+
+static struct file_operations dprintk_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = dprintk_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+ .write = dprintk_proc_write
+};
+
+/*
+ * Allocate a new dprintk_table for the given module
+ * and add it to the global list.
+ */
+static int dprintk_add_table(struct _dprintk *tab, unsigned int n,
+ struct module *mod)
+{
+ struct dprintk_table *dt;
+
+ dt = kzalloc(sizeof(*dt), GFP_KERNEL);
+ if (dt == NULL)
+ return -ENOMEM;
+ dt->module = mod;
+ dt->num_dprintks = n;
+ dt->dprintks = tab;
+
+ mutex_lock(&dprintk_lock);
+ list_add_tail(&dt->link, &dprintk_tables);
+ mutex_unlock(&dprintk_lock);
+
+ if (verbose)
+ printk(KERN_INFO "%u debug prints in module %s\n",
+ n, mod->name);
+ return n;
+}
+
+static void dprintk_table_free(struct dprintk_table *dt)
+{
+ list_del_init(&dt->link);
+ kfree(dt);
+}
+
+/*
+ * Called in response to a module being unloaded. Removes
+ * any dprintk_table's which point at the module.
+ */
+static int dprintk_remove_module(struct module *mod)
+{
+ struct dprintk_table *dt, *nextdt;
+ int ret = -ENOENT;
+
+ if (verbose)
+ printk(KERN_INFO "%s: removing module \"%s\"\n",
+ __FUNCTION__, mod->name);
+
+ mutex_lock(&dprintk_lock);
+ list_for_each_entry_safe(dt, nextdt, &dprintk_tables, link) {
+ if (dt->module == mod) {
+ dprintk_table_free(dt);
+ ret = 0;
+ }
+ }
+ mutex_unlock(&dprintk_lock);
+ return ret;
+}
+
+/*
+ * Called in response to the dprintk module itself being
+ * unloaded. Removes all dprintk_table's.
+ */
+static void dprintk_remove_all_tables(void)
+{
+ mutex_lock(&dprintk_lock);
+ while (!list_empty(&dprintk_tables)) {
+ struct dprintk_table *dt = list_entry(dprintk_tables.next,
+ struct dprintk_table,
+ link);
+ dprintk_table_free(dt);
+ }
+ mutex_unlock(&dprintk_lock);
+}
+
+static inline const char *section_name(const struct attribute *attr)
+{
+ return attr->name;
+}
+
+static inline void *section_address(const struct attribute *attr)
+{
+ return (void *)container_of(attr, struct module_sect_attr,
+ mattr.attr)->address;
+}
+
+static int dprintk_add_module(struct module *mod)
+{
+ const char *why = "";
+#ifdef CONFIG_KALLSYMS
+ unsigned int n;
+ struct attribute **gattr;
+ void *dprintk_start = NULL;
+ void *dprintk_end = NULL;
+ struct _dprintk *dp;
+
+ if (verbose)
+ printk(KERN_INFO "%s: adding module \"%s\"\n",
+ __FUNCTION__, mod->name);
+
+ if (mod->sect_attrs == NULL) {
+ why = "no section attributes";
+ goto nosyms;
+ }
+
+ /*
+ * First pass: find the .dprink section.
+ */
+ for (gattr = mod->sect_attrs->grp.attrs ; *gattr != NULL ; gattr++) {
+ if (!strcmp(section_name(*gattr), ".dprintk")) {
+ dprintk_start = section_address(*gattr);
+ break;
+ }
+ }
+ if (dprintk_start == NULL) {
+ why = "no .dprintk section";
+ goto nosyms;
+ }
+
+ /*
+ * Second pass: find the next loaded section.
+ *
+ * We don't assume anything about the ordering of mod->sect_attrs,
+ * which is probably silly.
+ *
+ * Note that we rely subtly on the .dprintk section not being the last
+ * loaded section in the .ko, but this seems not to be a problem as
+ * the module link process adds new sections.
+ */
+ for (gattr = mod->sect_attrs->grp.attrs ; *gattr != NULL ; gattr++) {
+ void *addr = section_address(*gattr);
+ if (!strcmp(section_name(*gattr), ".dprintk"))
+ continue;
+ if (addr < dprintk_start)
+ continue;
+ if (!dprintk_end || addr < dprintk_end)
+ dprintk_end = addr;
+ }
+ if (dprintk_end == NULL) {
+ why = "no section follows .dprintk section";
+ goto nosyms;
+ }
+
+ /* scan the table looking a sequence of valid magic numbers */
+ for (dp = (struct _dprintk *)dprintk_start ;
+ dp < (struct _dprintk *)dprintk_end ;
+ dp++) {
+ if (dp->magic != _DPRINTK_MAGIC)
+ break;
+ }
+ /* dp now points to first byte of padding beyond
+ * the end of the actual .dprintk section */
+ if (dp == (struct _dprintk *)dprintk_start) {
+ why = "no entries in .dprintk section";
+ goto nosyms;
+ }
+
+ /* Mention any leftover padding. Not that it matters a lot */
+ if (verbose && dp < (struct _dprintk *)dprintk_end)
+ printk(KERN_INFO "%s: ignoring %u bytes of "
+ "padding at end of .dprintk section\n",
+ __FUNCTION__, (unsigned)((u8 *)dprintk_end - (u8 *)dp));
+
+ n = dp - (struct _dprintk *)dprintk_start;
+ return dprintk_add_table((struct _dprintk *)dprintk_start, n, mod);
+
+nosyms:
+#else
+ why = "CONFIG_KALLSYMS not defined";
+#endif
+ if (verbose)
+ printk(KERN_ERR "%s: %s in module \"%s\" so "
+ "cannot find dprintks. Sorry.\n",
+ __FUNCTION__, why, mod->name);
+ return -ENOENT;
+}
+
+static void dprintk_add_module_hook(struct module *mod, void *closure)
+{
+ dprintk_add_module(mod);
+}
+
+/*
+ * Called from the base kernel module code when a module
+ * is loaded (in ToT, also when it's unloaded).
+ */
+static int dprintk_module_callback(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct module *mod = ptr;
+
+ if (verbose)
+ printk(KERN_INFO "%s: %lu %s\n",
+ __FUNCTION__, event, (mod ? mod->name : "null"));
+
+ switch (event) {
+ case MODULE_STATE_COMING:
+ dprintk_add_module(mod);
+ break;
+ case MODULE_STATE_GOING:
+ dprintk_remove_module(mod);
+ break;
+ default:
+ printk(KERN_ERR "%s: unexpected event %lu for module %s\n",
+ __FUNCTION__, event, (mod == NULL ? "null" : mod->name));
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dprintk_notifier = {
+ .notifier_call = dprintk_module_callback
+};
+
+/*
+ * Initialise the dprintk module.
+ */
+static int __init dprintk_init(void)
+{
+ struct proc_dir_entry *pde;
+
+ if (verbose)
+ printk(KERN_INFO "%s called\n", __FUNCTION__);
+
+ pde = proc_create("dprintk", 0644, NULL, &dprintk_proc_fops);
+ if (pde == NULL) {
+ printk(KERN_ERR "cannot create /proc/dprintk, "
+ "no dprintks will be available\n");
+ return -EINVAL;
+ }
+
+ /* Sadly, registering a notifier doesn't result
+ * in callbacks for existing modules. */
+ apply_modules(dprintk_add_module_hook, NULL);
+
+ register_module_notifier(&dprintk_notifier);
+ return 0;
+}
+
+/*
+ * Cleanup the dprintk module.
+ */
+static void __exit dprintk_cleanup(void)
+{
+ if (verbose)
+ printk(KERN_INFO "%s called\n", __FUNCTION__);
+ unregister_module_notifier(&dprintk_notifier);
+ remove_proc_entry("dprintk", NULL);
+ dprintk_remove_all_tables();
+}
+
+module_init(dprintk_init);
+module_exit(dprintk_cleanup);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Greg Banks <gnb@melbourne.sgi.com>");
+MODULE_DESCRIPTION("Provides a /proc interface for enabling "
+ "individual debugging prints");
+/* vim:set ai sw=8 sts=8: */
Index: bfields/kernel/Makefile
===================================================================
--- bfields.orig/kernel/Makefile
+++ bfields/kernel/Makefile
@@ -89,6 +89,7 @@ obj-$(CONFIG_FUNCTION_TRACER) += trace/
obj-$(CONFIG_TRACING) += trace/
obj-$(CONFIG_SMP) += sched_cpupri.o
obj-$(CONFIG_SLOW_WORK) += slow-work.o
+obj-$(CONFIG_DPRINTK) += dprintk.o

ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
Index: bfields/init/Kconfig
===================================================================
--- bfields.orig/init/Kconfig
+++ bfields/init/Kconfig
@@ -834,6 +834,21 @@ config SLOW_WORK
by a series of mkdirs and a create call, all of which have to touch
disk.

+config DPRINTK
+ default n
+ tristate "Allow runtime activation of individual debug prints"
+ help
+ Some kernel modules contain debugging prints, which emit
+ to the system log information which could be useful for
+ kernel developers and support staff. These are normally
+ disabled. This module provides /proc/dprintk which allows
+ a user to enable or disable these debug prints using a
+ simple query language. The language allows dprintks to
+ be activated or deactivated singly or severally by filename,
+ function, line number, and format string.
+
+ See Documentation/dprintk-howto.txt for more details.
+
endmenu # General setup

config HAVE_GENERIC_DMA_COHERENT
--
--
Greg Banks, P.Engineer, SGI Australian Software Group.
the brightly coloured sporks of revolution.
I don't speak for SGI.


\
 
 \ /
  Last update: 2009-01-20 02:37    [from the cache]
©2003-2011 Jasper Spaans