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    [W:0.082 / U:151.928 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site