> On Wed, 13 Jun 2007 17:06:57 +0200 holzheu <[EMAIL PROTECTED]> wrote: > Greetings, > > The operation of a Linux system sometimes requires to decode the > meaning of a specific kernel message, e.g. an error message of a > driver. Especially on our mainframe zSeries platform system > administrators want to have descriptions for Linux kernel messages. > They are used to that, because all other operating systems on that > platform like z/OS, z/VM or z/VSE have message catalogs with detailed > descriptions about the semantics of the messages. > > In general we think, that also for Linux it is a good thing to have > documentation for the most important kernel/driver messages. Even > kernel hackers not always are aware of the meaning of kernel messages > for components, which they don't know in detail. Most of the messages > are self explaining but sometimes you get something like "Clocksource > tsc unstable (delta = 7304132729 ns)" and you wonder if your system is > going to explode. > > Unfortunately currently there is no general infrastructure in the Linux > kernel for the documentation of messages. I worked on a proposal, how > that could be implemented in an easy way using the already existing > kernel-doc infrastructure and using printk. The proposal is as follows > > 1. We use message identifiers in order to map messages to message > descriptions. A message identifier consists out of a component name and > within that component unique message number. > > 2. Messages and message descriptions are maintained together in the > kernel sources in order to keep them up to date. Messages catalog are > generated automatically for exactly one kernel level. > > 3. A special tool checks, if messages are not documented or if there > are message descriptions without corresponding messages. > > 4. We use the already existing kernel-doc tool to generate an online > message catalog or e.g. a pdf for offline documentation. > > Current prototype implementation: > ================================= > > The structure of a kernel message is: <component>.<msg number>: <msg> > > * component: Name of the kernel or driver component e.g. "pci", "ide", > etc. > * msg number: Within the component unique number of a kernel message. > * msg: printk message > > New macros KMSG_ERR(), KMSG_WARN(), etc. are defined, which have to be > used in printk. These macros have as parameter the message number and > are using a per c-file defined macro KMSG_COMPONENT. > > Example: Define message 2 in component "kmsgtest": > > #define KMSG_COMPONENT "kmsgtest" > > void f(void) > { > printk(KMSG_ERR(1) "device %x not online\n", devno); > } > > The output of that kernel message would be: > "kmsgtest.1: device 4711 not online" > > The messages have to be documented within the C source file > in the following way: > > /** > * message > * @0: device number of device. > * > * Description: > * An operation has been performed on the msgtest device, but the > * device has not been set online. Therefore the operation failed > * > * User Response: > * Operator should set device online. > * Issue "chccwdev -e <device number>". > * > */ > > KMSG_DOC(kmsgtest, 2, "Device %x not online"); > > I created a patch for the kernel-doc tool so it can be used to generate > a catalog of all kernel messages: > > >> kernel-doc -man kmsgtest.c > kmsgtest.2 > >> man ./kmsgtest.2 > > # Kernel API(9) Linux Messages Kernel API(9) > # > # MESSAGE: > # kmsgtest.2: "device %x not online" > # > # Parameter: > # 1 Device number of device. > # > # Description: > # An operation has been performed on the msgtest device, but > # the device has not been set online. Therefore the operation failed. > # > # User Response: > # Operator should set device online. > # Issue "chccwdev -e <device number>". > # > # May 2007 kmsgtest.2 Kernel API(9) > > A nice thing would be to include the online kernel message catalog in > the kernel rpm. One possibility for that would be to have one man page > per message. If an operator finds the message kmsgtest.2 in > var/log/messages and wants to know what the message means, he simply > issues "man kmsgtest.2" and gets the description. > > To ensure that all messages are documented and there are no message > descriptions without corresponding messages, a checker tool is > provided. To enable message checking, in the toplevel Makefile the > following has to be added: > > CHECK = scripts/kmsg_check.pl check > > To enable message checking during kernel build, the "C" option has > to be used: > > >> make modules C=1 > CHK include/linux/version.h > CHK include/linux/utsrelease.h > CHECK drivers/kmsgtest/kmsgtest.c > drivers/kmsgtest/kmsgtest.c: Missing description for: kmsgtest.1 > drivers/kmsgtest/kmsgtest.c: Description without message for: kmsgtest.3 > > Please note, that the patch for the kernel-doc and kmsg-doc tools > is just a prototype and is neither complete nor perfect. > > Michael > > Acked-by: Martin Schwidefsky <[EMAIL PROTECTED]> > Acked-by: Heiko Carstens <[EMAIL PROTECTED]> > Signed-off-by: Michael Holzheu <[EMAIL PROTECTED]>
Your proposal is similar to one I made to some Japanese developers earlier this year. I was more modest, proposing that we - add an enhanced printk xxprintk(msgid, KERN_ERR "some text %d\n", some_number); - An externally managed database will provide translations, diagnostics, remedial actions, etc, keyed off the msgid string - Format and management of the msgid hasn't been clearly identified yet as far as I know. But all we really need is that each ID be unique, kernel-wide. We develop a simple tool which can check the whole tree for duplicated msgids. - From a practical day-to-day POV what this means is that a person from the kernel-messages team will prepare a patch for your driver which adds the msgids and you the driver author should take care not to break the tagging. This is deliberately designed to be minimum-impact upon general developers. We want a system in place where driver/fs/etc developers can concentrate on what they do, and kernel-message developers can as independently as possibe take care of what _they_ do. - A project has started up and there is a mailing list (cc'ed here) and meetings are happening at the LF collaboration summit this week. Please see https://lists.linux-foundation.org/mailman/listinfo/lf_kernel_messages and don't miss the next conference call ;) (argh. The lf_kernel_messages archives are subscriber-only and it could be that only members can post to it. Oh well. Please subscribe, and review the archives) > > Makefile | 5 > drivers/Makefile | 1 > drivers/kmsgtest/Makefile | 5 > drivers/kmsgtest/kmsgtest.c | 59 +++++++++++ > include/linux/kmsg.h | 32 ++++++ > scripts/kernel-doc | 116 +++++++++++++++++++++- > scripts/kmsg-doc | 231 > ++++++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 445 insertions(+), 4 deletions(-) > > diff -Naur linux-2.6.21/Makefile linux-2.6.21-kmsg/Makefile > --- linux-2.6.21/Makefile 2007-04-26 05:08:32.000000000 +0200 > +++ linux-2.6.21-kmsg/Makefile 2007-06-05 15:17:51.000000000 +0200 > @@ -293,9 +293,8 @@ > DEPMOD = /sbin/depmod > KALLSYMS = scripts/kallsyms > PERL = perl > -CHECK = sparse > - > -CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise > $(CF) > +CHECK = scripts/kmsg-doc check > +CHECKFLAGS = > MODFLAGS = -DMODULE > CFLAGS_MODULE = $(MODFLAGS) > AFLAGS_MODULE = $(MODFLAGS) > diff -Naur linux-2.6.21/drivers/Makefile linux-2.6.21-kmsg/drivers/Makefile > --- linux-2.6.21/drivers/Makefile 2007-04-26 05:08:32.000000000 +0200 > +++ linux-2.6.21-kmsg/drivers/Makefile 2007-06-05 15:17:51.000000000 > +0200 > @@ -8,6 +8,7 @@ > obj-$(CONFIG_PCI) += pci/ > obj-$(CONFIG_PARISC) += parisc/ > obj-$(CONFIG_RAPIDIO) += rapidio/ > +obj-m += kmsgtest/ > obj-y += video/ > obj-$(CONFIG_ACPI) += acpi/ > # PnP must come after ACPI since it will eventually need to check if acpi > diff -Naur linux-2.6.21/drivers/kmsgtest/Makefile > linux-2.6.21-kmsg/drivers/kmsgtest/Makefile > --- linux-2.6.21/drivers/kmsgtest/Makefile 1970-01-01 01:00:00.000000000 > +0100 > +++ linux-2.6.21-kmsg/drivers/kmsgtest/Makefile 2007-06-05 > 15:17:51.000000000 +0200 > @@ -0,0 +1,5 @@ > +# > +# Makefile for kernel message test module > +# > + > +obj-m += kmsgtest.o > diff -Naur linux-2.6.21/drivers/kmsgtest/kmsgtest.c > linux-2.6.21-kmsg/drivers/kmsgtest/kmsgtest.c > --- linux-2.6.21/drivers/kmsgtest/kmsgtest.c 1970-01-01 01:00:00.000000000 > +0100 > +++ linux-2.6.21-kmsg/drivers/kmsgtest/kmsgtest.c 2007-06-05 > 15:17:51.000000000 +0200 > @@ -0,0 +1,59 @@ > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/kmsg.h> > + > +static int devno = 0x4711; > +static int status = 1; > + > +#define KMSG_COMPONENT "kmsgtest" > + > +static int __init kmsgtest_init(void) > +{ > + printk(KMSG_INFO(1) "device %x has status %i\n", devno, status); > + printk(KMSG_ERR(2) "device %x not online\n", devno); > + > + return 0; > +} > + > +static void __exit kmsgtest_exit(void) > +{ > + printk("kmsgtest module exit\n"); > +} > + > +module_init(kmsgtest_init); > +module_exit(kmsgtest_exit); > + > +/** > + * message > + * @0: Device number of device > + * @1: Status of device > + * > + * Description: > + * Information message about the status of our virtual msgtest device. The > + * following values for the status parameter are available. > + * > + * 0 - Device is offline > + * > + * 1 - Device is online > + * > + * 2 - Device is broken > + * > + * User Response: > + * If device is broken, replace it or fix it. > + */ > + > +KMSG_DOC(kmsgtest, 1, "device %x has status %i"); > + > +/** > + * message > + * @0: Device number of device. > + * > + * Description: > + * An operation has been performed on the msgtest device, but the device has > + * not been set online. Therefore the operation failed. > + * > + * User Response: > + * Operator should set device online. Issue "chccwdev -e <device number>". > + */ > + > +KMSG_DOC(kmsgtest, 2, "device %x not online"); > diff -Naur linux-2.6.21/include/linux/kmsg.h > linux-2.6.21-kmsg/include/linux/kmsg.h > --- linux-2.6.21/include/linux/kmsg.h 1970-01-01 01:00:00.000000000 +0100 > +++ linux-2.6.21-kmsg/include/linux/kmsg.h 2007-06-05 15:17:51.000000000 > +0200 > @@ -0,0 +1,32 @@ > +#ifndef _LINUX_KMSG_H > +#define _LINUX_KMSG_H > + > +#ifdef __KMSG_CHECKER > + > +#define KMSG_EMERG(num) __KMSG_CHECK(EMERG, num) > +#define KMSG_ALERT(num) __KMSG_CHECK(ALERT, num) > +#define KMSG_CRIT(num) __KMSG_CHECK(CRIT, num) > +#define KMSG_ERR(num) __KMSG_CHECK(ERR, num) > +#define KMSG_WARNING(num) __KMSG_CHECK(WARNING, num) > +#define KMSG_NOTICE(num) __KMSG_CHECK(NOTICE, num) > +#define KMSG_INFO(num) __KMSG_CHECK(INFO, num) > +#define KMSG_DEBUG(num) __KMSG_CHECK(DEBUG, num) > + > +#define KMSG_DOC(comp, num, str) __KMSG_DOC(comp, num, str) > + > +#else > + > +#define KMSG_EMERG(num) KERN_EMERG KMSG_COMPONENT "." #num ": " > +#define KMSG_ALERT(num) KERN_ALERT KMSG_COMPONENT "." #num ": " > +#define KMSG_CRIT(num) KERN_CRIT KMSG_COMPONENT "." #num ": " > +#define KMSG_ERR(num) KERN_ERR KMSG_COMPONENT "." #num ": " > +#define KMSG_WARNING(num) KERN_WARNING KMSG_COMPONENT "." #num ": " > +#define KMSG_NOTICE(num) KERN_NOTICE KMSG_COMPONENT "." #num ": " > +#define KMSG_INFO(num) KERN_INFO KMSG_COMPONENT "." #num ": " > +#define KMSG_DEBUG(num) KERN_DEBUG KMSG_COMPONENT "." #num ": " > + > +#define KMSG_DOC(comp, num, str) > + > +#endif /* __KMSG_CHECKER */ > + > +#endif /* _LINUX_KMSG_H */ > diff -Naur linux-2.6.21/scripts/kernel-doc > linux-2.6.21-kmsg/scripts/kernel-doc > --- linux-2.6.21/scripts/kernel-doc 2007-04-26 05:08:32.000000000 +0200 > +++ linux-2.6.21-kmsg/scripts/kernel-doc 2007-06-05 15:17:51.000000000 > +0200 > @@ -256,7 +256,7 @@ > my $in_doc_sect; > > #declaration types: can be > -# 'function', 'struct', 'union', 'enum', 'typedef' > +# 'function', 'struct', 'union', 'enum', 'typedef', 'message' > my $decl_type; > > my $doc_special = "[EMAIL PROTECTED]&"; > @@ -1163,6 +1163,55 @@ > output_section_text(@_); > } > > +## > +# output message in text > +sub output_message_text(%) { > + my %args = %{$_[0]}; > + my ($parameter); > + > + print $args{'id'}.": \"".$args{'message'}."\"\n\n"; > + > + print "Parameters:\n\n"; > + foreach $parameter (@{$args{'parameterlist'}}) { > + ($parameter =~ /^#/) && next; > + my $parameter_name = $parameter; > + $parameter_name =~ s/\[.*//; > + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || > next; > + print "$parameter\n\t"; > + print $args{'parameterdescs'}{$parameter_name}."\n"; > + } > + output_section_text(@_); > +} > + > +## > +# output message in man > +sub output_message_man(%) { > + my %args = %{$_[0]}; > + my ($parameter, $section); > + > + print ".TH \"$args{'module'}\" 9 \"".$args{'id'}."\" \"$man_date\" > \"Linux Messages\" LINUX\n"; > + > + print ".SH MESSAGE\n"; > + print $args{'id'}.": "."\"".$args{'message'}."\"\n"; > + > + print ".SH Parameters\n"; > + foreach $parameter (@{$args{'parameterlist'}}) { > + ($parameter =~ /^#/) && next; > + > + my $parameter_name = $parameter; > + $parameter_name =~ s/\[.*//; > + > + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || > next; > + print ".IP \"".$parameter."\" 12\n"; > + output_highlight($args{'parameterdescs'}{$parameter_name}); > + } > + foreach $section (@{$args{'sectionlist'}}) { > + print ".SH \"$section\"\n"; > + output_highlight($args{'sections'}{$section}); > + print "\n"; > + } > +} > + > #output sections in text > sub output_section_text(%) { > my %args = %{$_[0]}; > @@ -1607,6 +1656,69 @@ > }); > } > > +sub create_parameterlist_msg($$) { > + my $args = shift; > + my $file = shift; > + my $splitter = "%"; > + my $type; > + my $param = 0; > + my $first = 1; > + > + # temporarily replace commas > + while ($args =~ /(\([^\),]+),/) { > + $args =~ s/(\([^\),]+),/$1#/g; > + } > + > + foreach my $arg (split($splitter, $args)) { > + if ($first) { > + $first = 0; > + next; > + } > + $type = substr($arg,0,1); > + > + # XXX introduce better type checking > + > + push_parameter($param, $type, $file); > + $param += 1; > + } > +} > + > +## > +# takes a message prototype and the name of the current file being > +# processed and spits out all the details stored in the global > +# arrays/hashes. > +sub dump_message($$) { > + my $x = shift; > + my $file = shift; > + > + if ($x =~/(KMSG_DOC)\(\s*(\w+)\s*\,\s*(\w+)\s*\,\s*\"(.*)\"\)/) { > + $declaration_name = "$2.$3"; > + my $members = $4; > + # strip comments: > + $members =~ s/\/\*.*?\*\///gos; > + > + create_parameterlist_msg($members, $file); > + > + output_declaration($declaration_name, > + 'message', > + {'message' => $members, > + 'id' => $declaration_name, > + 'module' => $modulename, > + 'parameterlist' => [EMAIL PROTECTED], > + 'parameterdescs' => \%parameterdescs, > + 'parametertypes' => \%parametertypes, > + 'sectionlist' => [EMAIL PROTECTED], > + 'sections' => \%sections, > + 'purpose' => $declaration_purpose, > + 'type' => $decl_type > + }); > + } > + else { > + print STDERR "Error(${file}:$.): Cannot parse message!\n"; > + ++$errors; > + } > +} > + > sub process_file($); > > # Read the file that maps relative names to absolute names for > @@ -1782,6 +1894,8 @@ > $decl_type = 'enum'; > } elsif ($identifier =~ m/^typedef/) { > $decl_type = 'typedef'; > + } elsif ($identifier =~ m/^message/) { > + $decl_type = 'message'; > } else { > $decl_type = 'function'; > } > diff -Naur linux-2.6.21/scripts/kmsg-doc linux-2.6.21-kmsg/scripts/kmsg-doc > --- linux-2.6.21/scripts/kmsg-doc 1970-01-01 01:00:00.000000000 +0100 > +++ linux-2.6.21-kmsg/scripts/kmsg-doc 2007-06-05 15:17:51.000000000 > +0200 > @@ -0,0 +1,231 @@ > +#!/usr/bin/perl > +# > +# Tool to check kernel messages > +# > +# Can be used in toplevel Linux Makefile in the following way: > +# > +# CHECK = scripts/kmsg-doc check > +# CHECKFLAGS = > +# > +# Note: This is just a prototype and neither perfect nor complete! > +# > +# Copyright (C) IBM Corp. 2007 > +# Author(s): Michael Holzheu <[EMAIL PROTECTED]> > +# > + > +sub create_message($$$$$) > +{ > + my ($sev, $component, $number, $text, $params) = @_; > + > + $text =~ s/\\n//; # remove trailing newline character > + $message_id = "$component.$number"; > + $messages{$message_count}->{'ID'} = $message_id; > + $messages{$message_count}->{'COMP'} = $component; > + $messages{$message_count}->{'NR'} = $number; > + $messages{$message_count}->{'MSG'} = $text; > + $messages{$message_count}->{'SEV'} = $sev; > + > + @parms = split(/[\s]*,[\s]*/,$params); > + $parm_count = 0; > + foreach $parm (@parms) { > + if (!($parm eq "")) { > + $messages{$message_count}->{'PARM_NAME'}->{$parm_count} > = $parm; > + $parm_count += 1; > + } > + } > + $messages{$message_count}->{'PARM_COUNT'} = $parm_count; > + $message_count += 1; > +} > + > +sub get_msgs($) > +{ > + my ($filename)[EMAIL PROTECTED]; > + > + $message_count = 0; > + open(FD, $filename); > + my @lines=<FD>; > + foreach $line (@lines) { > + if ($line =~ > /\s*printk\([\s]*__KMSG_CHECK\((.*)\,[\s]*(.*)\)[\s]*"(.*)"[\s]*(.*)[\s]*\);/) > { > + create_message($1, $component, $2, $3, $4); > + } > + } > +} > + > +sub get_descriptions($) > +{ > + my ($filename)[EMAIL PROTECTED]; > + my $desc_start; > + > + $description_count = 0; > + $desc_start = 0; > + open(FD, $filename); > + my @lines=<FD>; > + foreach $line (@lines) { > + if ($line =~ /#define [\s]*KMSG_COMPONENT [\s]*"(.*)"/) { > + $component = $1; > + } > + if ($line =~ /\s*\/\*\*$/) { > + $msg_start = 1; > + $parm_count = 0; > + next; > + } > + if (($msg_start == 1) && ($line =~ / \* message/)) { > + $desc_start = 1; > + next; > + } > + if ($line =~ / \*\//) { > + $desc_start = 0; > + next; > + } > + if ($desc_start == 1) { > + $descriptions{$description_count}->{'DESC'} .= "$line"; > + next; > + } > + if ($line =~ > + > /\s*KMSG_DOC\(\s*(.*)\s*\,\s*(.*)\s*\,\s*\"(.*)\"\s*\);/) { > + my $param_count = 0; > + my $first = 1; > + my $type; > + > + $descriptions{$description_count}->{'ID'} = "$1\.$2"; > + $descriptions{$description_count}->{'COMP'} = "$1"; > + $descriptions{$description_count}->{'NR'} = "$2"; > + $descriptions{$description_count}->{'MSG'} = "$3"; > + foreach my $arg (split("%", $3)) { > + if ($first) { > + $first = 0; > + next; > + } > + $type = substr($arg, 0, 1); > + > $descriptions{$description_count}->{'PARM_TYPE'}->{$param_count} = $type; > + $param_count += 1; > + } > + $descriptions{$description_count}->{'PARM_COUNT'} = > $param_count; > + $description_count += 1; > + } > + } > +} > + > +sub print_messages() > +{ > + for ($i = 0; $i < $message_count; $i++) { > + print "MESSAGE: $messages{$i}->{'ID'}\n"; > + } > +} > + > +sub print_descriptions($) > +{ > + my ($message_id)[EMAIL PROTECTED]; > + > + for ($i = 0; $i < $description_count; $i++) { > + if (($descriptions{$i}->{'ID'} eq $message_id) || $message_id > eq "all") { > + print > "==============================================================================\n"; > + print > "\[$descriptions{$i}->{'COMP'}\.$descriptions{$i}->{'NR'}\] > $descriptions{$i}->{'MSG'}\n"; > + print "\n"; > + print "Parameters:\n"; > + for ($j = 0; $j < $descriptions{$i}->{'PARM_COUNT'}; > $j++) { > + print " $descriptions{$i}->{'PARM_TYPE'}->{$j}: > $descriptions{$i}->{'PARM_DESC'}->{$j}\n"; > + } > + print "\n"; > + print "Description:\n"; > + print "$descriptions{$i}->{'DESC'}\n"; > + print > "==============================================================================\n"; > + } > + } > +} > + > +sub check_messages($) > +{ > + my ($filename)[EMAIL PROTECTED]; > + > + for ($i = 0; $i < $message_count; $i++) { > + $found = 0; > + for ($j = 0; $j < $description_count; $j++) { > + if ($messages{$i}->{'ID'} eq $descriptions{$j}->{'ID'}) > { > + $found = 1; > + last; > + } > + } > + if (!$found) { > + print STDERR "$filename: Missing description for: > $messages{$i}->{'ID'}\n"; > + } > + } > + for ($i = 0; $i < $description_count; $i++) { > + $found = 0; > + for ($j = 0; $j < $message_count; $j++) { > + if ($messages{$j}->{'ID'} eq $descriptions{$i}->{'ID'}) > { > + $found = 1; > + last; > + } > + } > + if (!$found) { > + print STDERR "$filename: Description without message > for: $descriptions{$i}->{'ID'}\n"; > + } > + } > +} > + > +sub print_templates() > +{ > + > + for ($i = 0; $i < $message_count; $i++) { > + $found = 0; > + for ($j = 0; $j < $description_count; $j++) { > + if ($messages{$i}->{'ID'} eq $descriptions{$j}->{'ID'}) > { > + $found = 1; > + } > + } > + if (!$found) { > + print "/**\n"; > + print " * message\n"; > + for ($k = 0; $k < $messages{$i}->{'PARM_COUNT'}; $k++) { > + print " * [EMAIL PROTECTED]: > $messages{$i}->{'PARM_NAME'}->{$k}\n"; > + } > + print " *\n"; > + print " * Description:\n"; > + print " *\n"; > + print " * User Response:\n"; > + print " */\n"; > + print "\n"; > + print "KMSG_DOC($messages{$i}->{'COMP'}, > $messages{$i}->{'NR'}, \"$messages{$i}->{'MSG'}\");\n" > + } > + } > +} > + > +sub usage() > +{ > + print "USAGE: kmsg_tool print | check <file>\n"; > + exit 1; > +} > + > +$option = shift; > + > +if ($option eq "check") { > + $gcc_options = "-E -D __KMSG_CHECKER "; > + do { > + $filename = $tmp; > + $tmp = shift; > + $tmp =~ s/\(/\\\(/; > + $tmp =~ s/\)/\\\)/; > + $gcc_options .= " $tmp"; > + } while (!($tmp eq "")); > + > + $gcc_options =~ s/-Wbitwise//; # XXX hack to remove -Wbitwise CHECKFLAG > + $gcc_options =~ s/-D__STDC__//; # XXX hack to remove -D__STDC__ > + $tmp_file = "$filename.msg"; > + system("gcc $gcc_options > $tmp_file"); > + get_descriptions($filename); > + get_msgs($tmp_file); > + check_messages($filename); > + print_templates(); > + system("rm $tmp_file"); > +} elsif ($option eq "print") { > + $filename = shift; > + do { > + print STDERR "Processing: $filename\n"; > + get_descriptions($filename); > + print_descriptions("all"); > + $filename = shift; > + } while (!($filename eq "")); > +} else { > + usage(); > +} > - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/