2014-12-20 22:52:20

by Daniel Walker

[permalink] [raw]
Subject: [PATCH] printk: add per console loglevel


This adds to to the console= command line options allowing the
addition of a per console log level setting.

examples,

console=ttyS0,ll4
console=tty0,ll6

This can be used on systems which have multiple serial consoles, but
it's desired for logging to be light on one and heavy on another.

Signed-off-by: Daniel Walker <[email protected]>
---
Documentation/kernel-parameters.txt | 24 ++++++++++++++++++------
include/linux/console.h | 1 +
kernel/printk/console_cmdline.h | 9 +++++----
kernel/printk/printk.c | 37 ++++++++++++++++++++++++++++++++++++-
4 files changed, 60 insertions(+), 11 deletions(-)

diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 4df73da..7e65d5b 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -696,30 +696,42 @@ bytes respectively. Such letter suffixes can also be entirely omitted.

console= [KNL] Output console device and options.

- tty<n> Use the virtual console device <n>.
+ tty<n>[,llX] Use the virtual console device <n>.

- ttyS<n>[,options]
- ttyUSB0[,options]
+ ttyS<n>[,options][,llX]
+ ttyUSB0[,options][,llX]
Use the specified serial port. The options are of
the form "bbbbpnf", where "bbbb" is the baud rate,
"p" is parity ("n", "o", or "e"), "n" is number of
bits, and "f" is flow control ("r" for RTS or
omit it). Default is "9600n8".

+ "llX" explained below,
+
See Documentation/serial-console.txt for more
information. See
Documentation/networking/netconsole.txt for an
alternative.

- uart[8250],io,<addr>[,options]
- uart[8250],mmio,<addr>[,options]
+ uart[8250],io,<addr>[,options][,llX]
+ uart[8250],mmio,<addr>[,options][,llX]
Start an early, polled-mode console on the 8250/16550
UART at the specified I/O port or MMIO address,
switching to the matching ttyS device later. The
options are the same as for ttyS, above.
- hvc<n> Use the hypervisor console device <n>. This is for
+
+ "llX" explained below,
+
+ hvc<n>[,llX]
+ Use the hypervisor console device <n>. This is for
both Xen and PowerPC hypervisors.

+ "llX" is used to define the loglevel per console. The "X"
+ defines the loglevel number for this console. The usage
+ is similar to the "loglevel=" option, and the effect is
+ the same only per console. The "loglevel=" option takes
+ precedence of this option.
+
If the device connected to the port is not a TTY but a braille
device, prepend "brl," before the device type, for instance
console=brl,ttyS0
diff --git a/include/linux/console.h b/include/linux/console.h
index 7571a16..99020d5 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -118,6 +118,7 @@ static inline int con_debug_leave(void)

struct console {
char name[16];
+ int loglevel;
void (*write)(struct console *, const char *, unsigned);
int (*read)(struct console *, char *, unsigned);
struct tty_driver *(*device)(struct console *, int *);
diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h
index cbd69d8..6f6f98f 100644
--- a/kernel/printk/console_cmdline.h
+++ b/kernel/printk/console_cmdline.h
@@ -3,11 +3,12 @@

struct console_cmdline
{
- char name[8]; /* Name of the driver */
- int index; /* Minor dev. to use */
- char *options; /* Options for the driver */
+ char name[8]; /* Name of the driver */
+ int index; /* Minor dev. to use */
+ int loglevel; /* Log level for this console */
+ char *options; /* Options for the driver */
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
- char *brl_options; /* Options for braille driver */
+ char *brl_options; /* Options for braille driver */
#endif
};

diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 02d6b6d..218d94d 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1414,6 +1414,9 @@ static void call_console_drivers(int level, const char *text, size_t len)
if (!cpu_online(smp_processor_id()) &&
!(con->flags & CON_ANYTIME))
continue;
+ if (con->loglevel && !ignore_loglevel &&
+ level >= con->loglevel)
+ continue;
con->write(con, text, len);
}
}
@@ -1925,6 +1928,33 @@ asmlinkage __visible void early_printk(const char *fmt, ...)
}
#endif

+
+char *setup_per_console_loglevel(struct console_cmdline *con, char *options)
+{
+ int size = options ? strlen(options) : 0;
+
+ if (size >= 3 &&
+ options[size - 3] == 'l' &&
+ options[size - 2] == 'l' &&
+ options[size - 1] >= '1' && options[size - 1] <= '9') {
+ con->loglevel = options[size - 1] - '0';
+
+ /* Catch if the user added a comma after some serial console
+ * options,
+ * i.e. console=ttyS0,9600n8,ll3
+ * the first comma is gone at this point, but we need to delete
+ * the second one.
+ */
+ if (size > 3 && options[size - 4] == ',')
+ options[size - 4] = 0;
+ else
+ options[size - 3] = 0;
+ } else
+ con->loglevel = 0;
+
+ return options;
+}
+
static int __add_preferred_console(char *name, int idx, char *options,
char *brl_options)
{
@@ -1949,12 +1979,15 @@ static int __add_preferred_console(char *name, int idx, char *options,
if (!brl_options)
selected_console = i;
strlcpy(c->name, name, sizeof(c->name));
- c->options = options;
+
+ c->options = setup_per_console_loglevel(c, options);
+
braille_set_options(c, brl_options);

c->index = idx;
return 0;
}
+
/*
* Set up a console. Called via do_early_param() in init/main.c
* for each "console=" parameter in the boot command line.
@@ -2478,6 +2511,8 @@ void register_console(struct console *newcon)
if (newcon->setup &&
newcon->setup(newcon, console_cmdline[i].options) != 0)
break;
+
+ newcon->loglevel = c->loglevel;
newcon->flags |= CON_ENABLED;
newcon->index = c->index;
if (i == selected_console) {
--
1.9.1


2014-12-21 18:48:11

by Bruno Prémont

[permalink] [raw]
Subject: Re: [PATCH] printk: add per console loglevel

On Sat, 20 December 2014 [email protected] wrote:
> This adds to to the console= command line options allowing the
> addition of a per console log level setting.
>
> examples,
>
> console=ttyS0,ll4
> console=tty0,ll6
>
> This can be used on systems which have multiple serial consoles, but
> it's desired for logging to be light on one and heavy on another.

Looks useful to me.
What would be the best way to make these per-console loglevels
configurable at runtime? `dmesg -n $LEVEL` only affects the global
limit.

One drawback to letting global loglevel have precedence is that
all consoles that should not get detailed log messages need to have
their loglevel explicitly lowered and it's not possible to have
one console forced to show more than the global loglevel.


An approach I would prefer is to have all consoles follow global
loglevel except when something different had been explicitly requested
for them.
This way a single console can be added later on (e.g. netconsole)
and set to pass through debug messages without affecting anyone else.

Bruno

> Signed-off-by: Daniel Walker <[email protected]>
> ---
> Documentation/kernel-parameters.txt | 24 ++++++++++++++++++------
> include/linux/console.h | 1 +
> kernel/printk/console_cmdline.h | 9 +++++----
> kernel/printk/printk.c | 37 ++++++++++++++++++++++++++++++++++++-
> 4 files changed, 60 insertions(+), 11 deletions(-)
>
> diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
> index 4df73da..7e65d5b 100644
> --- a/Documentation/kernel-parameters.txt
> +++ b/Documentation/kernel-parameters.txt
> @@ -696,30 +696,42 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
>
> console= [KNL] Output console device and options.
>
> - tty<n> Use the virtual console device <n>.
> + tty<n>[,llX] Use the virtual console device <n>.
>
> - ttyS<n>[,options]
> - ttyUSB0[,options]
> + ttyS<n>[,options][,llX]
> + ttyUSB0[,options][,llX]
> Use the specified serial port. The options are of
> the form "bbbbpnf", where "bbbb" is the baud rate,
> "p" is parity ("n", "o", or "e"), "n" is number of
> bits, and "f" is flow control ("r" for RTS or
> omit it). Default is "9600n8".
>
> + "llX" explained below,
> +
> See Documentation/serial-console.txt for more
> information. See
> Documentation/networking/netconsole.txt for an
> alternative.
>
> - uart[8250],io,<addr>[,options]
> - uart[8250],mmio,<addr>[,options]
> + uart[8250],io,<addr>[,options][,llX]
> + uart[8250],mmio,<addr>[,options][,llX]
> Start an early, polled-mode console on the 8250/16550
> UART at the specified I/O port or MMIO address,
> switching to the matching ttyS device later. The
> options are the same as for ttyS, above.
> - hvc<n> Use the hypervisor console device <n>. This is for
> +
> + "llX" explained below,
> +
> + hvc<n>[,llX]
> + Use the hypervisor console device <n>. This is for
> both Xen and PowerPC hypervisors.
>
> + "llX" is used to define the loglevel per console. The "X"
> + defines the loglevel number for this console. The usage
> + is similar to the "loglevel=" option, and the effect is
> + the same only per console. The "loglevel=" option takes
> + precedence of this option.
> +
> If the device connected to the port is not a TTY but a braille
> device, prepend "brl," before the device type, for instance
> console=brl,ttyS0
> diff --git a/include/linux/console.h b/include/linux/console.h
> index 7571a16..99020d5 100644
> --- a/include/linux/console.h
> +++ b/include/linux/console.h
> @@ -118,6 +118,7 @@ static inline int con_debug_leave(void)
>
> struct console {
> char name[16];
> + int loglevel;
> void (*write)(struct console *, const char *, unsigned);
> int (*read)(struct console *, char *, unsigned);
> struct tty_driver *(*device)(struct console *, int *);
> diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h
> index cbd69d8..6f6f98f 100644
> --- a/kernel/printk/console_cmdline.h
> +++ b/kernel/printk/console_cmdline.h
> @@ -3,11 +3,12 @@
>
> struct console_cmdline
> {
> - char name[8]; /* Name of the driver */
> - int index; /* Minor dev. to use */
> - char *options; /* Options for the driver */
> + char name[8]; /* Name of the driver */
> + int index; /* Minor dev. to use */
> + int loglevel; /* Log level for this console */
> + char *options; /* Options for the driver */
> #ifdef CONFIG_A11Y_BRAILLE_CONSOLE
> - char *brl_options; /* Options for braille driver */
> + char *brl_options; /* Options for braille driver */
> #endif
> };
>
> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> index 02d6b6d..218d94d 100644
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -1414,6 +1414,9 @@ static void call_console_drivers(int level, const char *text, size_t len)
> if (!cpu_online(smp_processor_id()) &&
> !(con->flags & CON_ANYTIME))
> continue;
> + if (con->loglevel && !ignore_loglevel &&
> + level >= con->loglevel)
> + continue;
> con->write(con, text, len);
> }
> }
> @@ -1925,6 +1928,33 @@ asmlinkage __visible void early_printk(const char *fmt, ...)
> }
> #endif
>
> +
> +char *setup_per_console_loglevel(struct console_cmdline *con, char *options)
> +{
> + int size = options ? strlen(options) : 0;
> +
> + if (size >= 3 &&
> + options[size - 3] == 'l' &&
> + options[size - 2] == 'l' &&
> + options[size - 1] >= '1' && options[size - 1] <= '9') {
> + con->loglevel = options[size - 1] - '0';
> +
> + /* Catch if the user added a comma after some serial console
> + * options,
> + * i.e. console=ttyS0,9600n8,ll3
> + * the first comma is gone at this point, but we need to delete
> + * the second one.
> + */
> + if (size > 3 && options[size - 4] == ',')
> + options[size - 4] = 0;
> + else
> + options[size - 3] = 0;
> + } else
> + con->loglevel = 0;
> +
> + return options;
> +}
> +
> static int __add_preferred_console(char *name, int idx, char *options,
> char *brl_options)
> {
> @@ -1949,12 +1979,15 @@ static int __add_preferred_console(char *name, int idx, char *options,
> if (!brl_options)
> selected_console = i;
> strlcpy(c->name, name, sizeof(c->name));
> - c->options = options;
> +
> + c->options = setup_per_console_loglevel(c, options);
> +
> braille_set_options(c, brl_options);
>
> c->index = idx;
> return 0;
> }
> +
> /*
> * Set up a console. Called via do_early_param() in init/main.c
> * for each "console=" parameter in the boot command line.
> @@ -2478,6 +2511,8 @@ void register_console(struct console *newcon)
> if (newcon->setup &&
> newcon->setup(newcon, console_cmdline[i].options) != 0)
> break;
> +
> + newcon->loglevel = c->loglevel;
> newcon->flags |= CON_ENABLED;
> newcon->index = c->index;
> if (i == selected_console) {


Attachments:
(No filename) (648.00 B)
OpenPGP digital signature

2014-12-21 19:03:28

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH] printk: add per console loglevel

On Sun, 2014-12-21 at 19:47 +0100, Bruno Pr?mont wrote:
> On Sat, 20 December 2014 [email protected] wrote:
> > This adds to to the console= command line options allowing the
> > addition of a per console log level setting.
> >
> > examples,
> >
> > console=ttyS0,ll4
> > console=tty0,ll6
> >
> > This can be used on systems which have multiple serial consoles, but
> > it's desired for logging to be light on one and heavy on another.
>
> Looks useful to me.

I think this is reasonable to, but for consistency
with other loglevel uses, I suggest using
loglevel=<level> instead of ll<d>

2014-12-22 01:49:07

by Lennart Sorensen

[permalink] [raw]
Subject: Re: [PATCH] printk: add per console loglevel

On Sun, Dec 21, 2014 at 11:03:24AM -0800, Joe Perches wrote:
> On Sun, 2014-12-21 at 19:47 +0100, Bruno Prémont wrote:
> > On Sat, 20 December 2014 [email protected] wrote:
> > > This adds to to the console= command line options allowing the
> > > addition of a per console log level setting.
> > >
> > > examples,
> > >
> > > console=ttyS0,ll4
> > > console=tty0,ll6
> > >
> > > This can be used on systems which have multiple serial consoles, but
> > > it's desired for logging to be light on one and heavy on another.
> >
> > Looks useful to me.
>
> I think this is reasonable to, but for consistency
> with other loglevel uses, I suggest using
> loglevel=<level> instead of ll<d>

Oh those are l not 1. I read that as ,114 and ,116 and was wondering
what that meant.

--
Len Sorensen

2014-12-23 16:15:11

by Daniel Walker

[permalink] [raw]
Subject: Re: [PATCH] printk: add per console loglevel

On Sun, Dec 21, 2014 at 11:03:24AM -0800, Joe Perches wrote:
> On Sun, 2014-12-21 at 19:47 +0100, Bruno Pr?mont wrote:
> > On Sat, 20 December 2014 [email protected] wrote:
> > > This adds to to the console= command line options allowing the
> > > addition of a per console log level setting.
> > >
> > > examples,
> > >
> > > console=ttyS0,ll4
> > > console=tty0,ll6
> > >
> > > This can be used on systems which have multiple serial consoles, but
> > > it's desired for logging to be light on one and heavy on another.
> >
> > Looks useful to me.
>
> I think this is reasonable to, but for consistency
> with other loglevel uses, I suggest using
> loglevel=<level> instead of ll<d>
>

I can try it, I didn't do it initially because I didn't want to confuse the parsing of the other
parameters ..

Daniel

2014-12-23 16:23:32

by Daniel Walker

[permalink] [raw]
Subject: Re: [PATCH] printk: add per console loglevel

On Sun, Dec 21, 2014 at 07:47:53PM +0100, Bruno Pr?mont wrote:
> On Sat, 20 December 2014 [email protected] wrote:
> > This adds to to the console= command line options allowing the
> > addition of a per console log level setting.
> >
> > examples,
> >
> > console=ttyS0,ll4
> > console=tty0,ll6
> >
> > This can be used on systems which have multiple serial consoles, but
> > it's desired for logging to be light on one and heavy on another.
>
> Looks useful to me.
> What would be the best way to make these per-console loglevels
> configurable at runtime? `dmesg -n $LEVEL` only affects the global
> limit.
>
> One drawback to letting global loglevel have precedence is that
> all consoles that should not get detailed log messages need to have
> their loglevel explicitly lowered and it's not possible to have
> one console forced to show more than the global loglevel.

Right, reason for the patch.

>
> An approach I would prefer is to have all consoles follow global
> loglevel except when something different had been explicitly requested
> for them.

This patch is mostly like this. It breaks down when you set the "llX"
parameter to something above KERN_NOTICE, and the global one is also
set above that.

> This way a single console can be added later on (e.g. netconsole)
> and set to pass through debug messages without affecting anyone else.

Not following the "added later" part.

Daniel

2014-12-23 21:13:36

by Bruno Prémont

[permalink] [raw]
Subject: Re: [PATCH] printk: add per console loglevel

On Tue, 23 December 2014 [email protected] wrote:
> On Sun, Dec 21, 2014 at 07:47:53PM +0100, Bruno Prémont wrote:
> > On Sat, 20 December 2014 [email protected] wrote:
> > > This adds to to the console= command line options allowing the
> > > addition of a per console log level setting.
> > >
> > > examples,
> > >
> > > console=ttyS0,ll4
> > > console=tty0,ll6
> > >
> > > This can be used on systems which have multiple serial consoles, but
> > > it's desired for logging to be light on one and heavy on another.
> >
> > Looks useful to me.
> > What would be the best way to make these per-console loglevels
> > configurable at runtime? `dmesg -n $LEVEL` only affects the global
> > limit.
> >
> > One drawback to letting global loglevel have precedence is that
> > all consoles that should not get detailed log messages need to have
> > their loglevel explicitly lowered and it's not possible to have
> > one console forced to show more than the global loglevel.
>
> Right, reason for the patch.
>
> >
> > An approach I would prefer is to have all consoles follow global
> > loglevel except when something different had been explicitly requested
> > for them.
>
> This patch is mostly like this. It breaks down when you set the "llX"
> parameter to something above KERN_NOTICE, and the global one is also
> set above that.

In your patch global filter applies first, then what passes global filter
gets filtered with the per-console filter.

So I can't have `dmesg -n 3` and "console=ttyS0,ll8 console=tty0"
to get my debugging output over serial console.

> > This way a single console can be added later on (e.g. netconsole)
> > and set to pass through debug messages without affecting anyone else.

Netconsole console can be added at any time when system is running
either through loading of its module or using dynamic configuration
via configfs.

See also my patches.

Thanks,
Bruno

2014-12-23 21:22:16

by Bruno Prémont

[permalink] [raw]
Subject: [PATCH 1/3] printk: Use a flag to indicate console-private loglevel

In order to set loglevel for a given console that is not affected by
global loglevel as adjusted via syslog(2), add a flag to the console and
choose the level to match against msg level depending on this flag.

Signed-off-by: Bruno Prémont <[email protected]>
---
This depends on Daniel's patch "printk: add per console loglevel"
and modifies the way its filtering is applied.


include/linux/console.h | 1 +
kernel/printk/printk.c | 12 ++++++------
2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/include/linux/console.h b/include/linux/console.h
index 99020d5..f3a8996 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -115,6 +115,7 @@ static inline int con_debug_leave(void)
#define CON_BOOT (8)
#define CON_ANYTIME (16) /* Safe to call when cpu is offline */
#define CON_BRL (32) /* Used for a braille device */
+#define CON_LOGLEVEL (64) /* Per-console log-level filtering */

struct console {
char name[16];
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 218d94d..8f09f30 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1399,12 +1399,12 @@ static void call_console_drivers(int level, const char *text, size_t len)

trace_console(text, len);

- if (level >= console_loglevel && !ignore_loglevel)
- return;
if (!console_drivers)
return;

for_each_console(con) {
+ if (!ignore_loglevel && level >= (con->flags & CON_LOGLEVEL ? con->loglevel : console_loglevel))
+ continue;
if (exclusive_console && con != exclusive_console)
continue;
if (!(con->flags & CON_ENABLED))
@@ -1414,9 +1414,6 @@ static void call_console_drivers(int level, const char *text, size_t len)
if (!cpu_online(smp_processor_id()) &&
!(con->flags & CON_ANYTIME))
continue;
- if (con->loglevel && !ignore_loglevel &&
- level >= con->loglevel)
- continue;
con->write(con, text, len);
}
}
@@ -2512,7 +2509,10 @@ void register_console(struct console *newcon)
newcon->setup(newcon, console_cmdline[i].options) != 0)
break;

- newcon->loglevel = c->loglevel;
+ if (c->loglevel) {
+ newcon->loglevel = c->loglevel;
+ newcon->flags |= CON_LOGLEVEL;
+ }
newcon->flags |= CON_ENABLED;
newcon->index = c->index;
if (i == selected_console) {
--
2.0.4

2014-12-23 21:27:45

by Bruno Prémont

[permalink] [raw]
Subject: [PATCH 2/3] netconsole: make loglevel configurable per target

Switch to registering a new console for each target so that loglevel
based message filtering can be set individually for each target.

This adds a new loglevel= option to netconsole module parameter and also
add a configfs file to configure the loglevel.

The loglevel conf be adjusted at any time for synamic netconsole
consoles.

Signed-off-by: Bruno Prémont <[email protected]>
---
Note: only configuration via configfs has been runtime-tested.

Documentation/networking/netconsole.txt | 11 ++-
drivers/net/netconsole.c | 114 +++++++++++++++++++++++---------
2 files changed, 94 insertions(+), 31 deletions(-)

diff --git a/Documentation/networking/netconsole.txt b/Documentation/networking/netconsole.txt
index a5d574a..c1df516 100644
--- a/Documentation/networking/netconsole.txt
+++ b/Documentation/networking/netconsole.txt
@@ -24,7 +24,7 @@ Sender and receiver configuration:
It takes a string configuration parameter "netconsole" in the
following format:

- netconsole=[src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
+ netconsole=[src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr][,loglevel=level]

where
src-port source for UDP packets (defaults to 6665)
@@ -33,6 +33,9 @@ following format:
tgt-port port for logging agent (6666)
tgt-ip IP address for logging agent
tgt-macaddr ethernet MAC address for logging agent (broadcast)
+ level limit messages printed to those whose loglevel is
+ smaller that value, range is 1 to 8
+ If missing, loglevel limit as set via syslog(1) applies

Examples:

@@ -114,11 +117,16 @@ The interface exposes these parameters of a netconsole target to userspace:
remote_ip Remote agent's IP address (read-write)
local_mac Local interface's MAC address (read-only)
remote_mac Remote agent's MAC address (read-write)
+ loglevel Console loglevel filter (read-write)

The "enabled" attribute is also used to control whether the parameters of
a target can be updated or not -- you can modify the parameters of only
disabled targets (i.e. if "enabled" is 0).

+The "loglevel" parameter can be set at any time. When set to "0" it will
+read back as empty string and global loglevel filter applies. Otherwise
+specified loglevel filter applies.
+
To update a target's parameters:

cat enabled # check if enabled is 1
@@ -164,6 +172,7 @@ priority messages to the console. You can change this at runtime using:

dmesg -n 8

+or by specifying netconsole loglevel parameter
or by specifying "debug" on the kernel command line at boot, to send
all kernel messages to the console. A specific value for this parameter
can also be set using the "loglevel" kernel boot option. See the
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index ba2f5e7..a96cd8e 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -103,10 +103,12 @@ struct netconsole_target {
#ifdef CONFIG_NETCONSOLE_DYNAMIC
struct config_item item;
#endif
+ struct console console;
int enabled;
struct mutex mutex;
struct netpoll np;
};
+static void write_msg(struct console *con, const char *msg, unsigned int len);

#ifdef CONFIG_NETCONSOLE_DYNAMIC

@@ -171,6 +173,7 @@ static struct netconsole_target *alloc_param_target(char *target_config)
{
int err = -ENOMEM;
struct netconsole_target *nt;
+ char *loglevel;

/*
* Allocate and initialize with defaults.
@@ -186,8 +189,26 @@ static struct netconsole_target *alloc_param_target(char *target_config)
nt->np.remote_port = 6666;
mutex_init(&nt->mutex);
memset(nt->np.remote_mac, 0xff, ETH_ALEN);
+ snprintf(nt->console.name, sizeof(nt->console.name), "netcon");
+ /* Dump existing printks when we register */
+ nt->console.flags = CON_ENABLED | CON_PRINTBUFFER;
+ nt->console.write = write_msg;

/* Parse parameters and setup netpoll */
+ loglevel = strstr(target_config, ",loglevel=");
+ if (loglevel) {
+ int level;
+ err = kstrtoint(loglevel+10, 10, &level);
+ if (err < 0)
+ goto fail;
+ if (level < 1 || level > 8) {
+ err = -EINVAL;
+ goto fail;
+ }
+ nt->console.loglevel = level;
+ nt->console.flags |= CON_LOGLEVEL;
+ *loglevel = '\0';
+ }
err = netpoll_parse_options(&nt->np, target_config);
if (err)
goto fail;
@@ -228,6 +249,7 @@ static void free_param_target(struct netconsole_target *nt)
* | remote_ip
* | local_mac
* | remote_mac
+ * | loglevel
* |
* <target>/...
*/
@@ -301,6 +323,14 @@ static ssize_t show_remote_mac(struct netconsole_target *nt, char *buf)
return snprintf(buf, PAGE_SIZE, "%pM\n", nt->np.remote_mac);
}

+static ssize_t show_loglevel(struct netconsole_target *nt, char *buf)
+{
+ if (nt->console.flags & CON_LOGLEVEL)
+ return snprintf(buf, PAGE_SIZE, "%u\n", nt->console.loglevel);
+ else
+ return snprintf(buf, PAGE_SIZE, "\n");
+}
+
/*
* This one is special -- targets created through the configfs interface
* are not enabled (and the corresponding netpoll activated) by default.
@@ -338,6 +368,8 @@ static ssize_t store_enabled(struct netconsole_target *nt,
if (err)
return err;

+ nt->enabled = 1;
+ register_console(&nt->console);
pr_info("netconsole: network logging started\n");
} else { /* 0 */
/* We need to disable the netconsole before cleaning it up
@@ -347,11 +379,11 @@ static ssize_t store_enabled(struct netconsole_target *nt,
spin_lock_irqsave(&target_list_lock, flags);
nt->enabled = 0;
spin_unlock_irqrestore(&target_list_lock, flags);
+ unregister_console(&nt->console);
netpoll_cleanup(&nt->np);
+ nt->console.flags = nt->console.flags & ~CON_PRINTBUFFER;
}

- nt->enabled = enabled;
-
return strnlen(buf, count);
}

@@ -494,6 +526,27 @@ static ssize_t store_remote_mac(struct netconsole_target *nt,
return strnlen(buf, count);
}

+static ssize_t store_loglevel(struct netconsole_target *nt,
+ const char *buf,
+ size_t count)
+{
+ int rv, level;
+
+ rv = kstrtoint(buf, 10, &level);
+ if (rv < 0)
+ return rv;
+ if (level < 0 || level > 8)
+ return -EINVAL;
+
+ if (level > 0) {
+ nt->console.loglevel = level;
+ nt->console.flags |= CON_LOGLEVEL;
+ } else
+ nt->console.flags &= ~CON_LOGLEVEL;
+
+ return strnlen(buf, count);
+}
+
/*
* Attribute definitions for netconsole_target.
*/
@@ -514,6 +567,7 @@ NETCONSOLE_TARGET_ATTR_RW(local_ip);
NETCONSOLE_TARGET_ATTR_RW(remote_ip);
NETCONSOLE_TARGET_ATTR_RO(local_mac);
NETCONSOLE_TARGET_ATTR_RW(remote_mac);
+NETCONSOLE_TARGET_ATTR_RW(loglevel);

static struct configfs_attribute *netconsole_target_attrs[] = {
&netconsole_target_enabled.attr,
@@ -524,6 +578,7 @@ static struct configfs_attribute *netconsole_target_attrs[] = {
&netconsole_target_remote_ip.attr,
&netconsole_target_local_mac.attr,
&netconsole_target_remote_mac.attr,
+ &netconsole_target_loglevel.attr,
NULL,
};

@@ -605,6 +660,10 @@ static struct config_item *make_netconsole_target(struct config_group *group,
nt->np.remote_port = 6666;
mutex_init(&nt->mutex);
memset(nt->np.remote_mac, 0xff, ETH_ALEN);
+ snprintf(nt->console.name, sizeof(nt->console.name), "netcon-%s", name);
+ nt->console.flags = CON_ENABLED;
+ nt->console.write = write_msg;
+ nt->console.loglevel = 0;

/* Initialize the config_item member */
config_item_init_type_name(&nt->item, name, &netconsole_target_type);
@@ -626,6 +685,7 @@ static void drop_netconsole_target(struct config_group *group,
spin_lock_irqsave(&target_list_lock, flags);
list_del(&nt->list);
spin_unlock_irqrestore(&target_list_lock, flags);
+ unregister_console(&nt->console);

/*
* The target may have never been enabled, or was manually disabled
@@ -732,7 +792,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
{
int frag, left;
unsigned long flags;
- struct netconsole_target *nt;
+ struct netconsole_target *nt = container_of(con, struct netconsole_target, console);
const char *tmp;

if (oops_only && !oops_in_progress)
@@ -742,34 +802,26 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
return;

spin_lock_irqsave(&target_list_lock, flags);
- list_for_each_entry(nt, &target_list, list) {
- netconsole_target_get(nt);
- if (nt->enabled && netif_running(nt->np.dev)) {
- /*
- * We nest this inside the for-each-target loop above
- * so that we're able to get as much logging out to
- * at least one target if we die inside here, instead
- * of unnecessarily keeping all targets in lock-step.
- */
- tmp = msg;
- for (left = len; left;) {
- frag = min(left, MAX_PRINT_CHUNK);
- netpoll_send_udp(&nt->np, tmp, frag);
- tmp += frag;
- left -= frag;
- }
+ netconsole_target_get(nt);
+ if (nt->enabled && netif_running(nt->np.dev)) {
+ /*
+ * We nest this inside the for-each-target loop above
+ * so that we're able to get as much logging out to
+ * at least one target if we die inside here, instead
+ * of unnecessarily keeping all targets in lock-step.
+ */
+ tmp = msg;
+ for (left = len; left;) {
+ frag = min(left, MAX_PRINT_CHUNK);
+ netpoll_send_udp(&nt->np, tmp, frag);
+ tmp += frag;
+ left -= frag;
}
- netconsole_target_put(nt);
}
+ netconsole_target_put(nt);
spin_unlock_irqrestore(&target_list_lock, flags);
}

-static struct console netconsole = {
- .name = "netcon",
- .flags = CON_ENABLED,
- .write = write_msg,
-};
-
static int __init init_netconsole(void)
{
int err;
@@ -785,8 +837,6 @@ static int __init init_netconsole(void)
err = PTR_ERR(nt);
goto fail;
}
- /* Dump existing printks when we register */
- netconsole.flags |= CON_PRINTBUFFER;

spin_lock_irqsave(&target_list_lock, flags);
list_add(&nt->list, &target_list);
@@ -802,7 +852,9 @@ static int __init init_netconsole(void)
if (err)
goto undonotifier;

- register_console(&netconsole);
+ list_for_each_entry_safe(nt, tmp, &target_list, list) {
+ register_console(&nt->console);
+ }
pr_info("network logging started\n");

return err;
@@ -830,7 +882,9 @@ static void __exit cleanup_netconsole(void)
{
struct netconsole_target *nt, *tmp;

- unregister_console(&netconsole);
+ list_for_each_entry_safe(nt, tmp, &target_list, list) {
+ unregister_console(&nt->console);
+ }
dynamic_netconsole_exit();
unregister_netdevice_notifier(&netconsole_netdev_notifier);

--
2.0.4

2014-12-23 21:37:29

by Bruno Prémont

[permalink] [raw]
Subject: [PATCH 3/3] netconsole: New console flag to dump full log buffer

Be default only log messages not yet seen by userspace are output when
enabling netconsole via module parameter (none when enabling dynamic
targets).

When using netconsole to centrally log kernel messages it's of interest
to have the whole kernel log sent over the same medium, even if netconsole
setup happens rather late during system boot. The big advantage of netconsole
over syslog for this task is that it usually allow catching much more
messages when system crashes/panics.

This causes dynamic netconsoles to request full kernel log when first
enabled.

Signed-off-by: Bruno Prémont <[email protected]>
---
Note, with this kernel is too quick at sending packets for some receiving
machines to catch all of them. Either packets get dropped by the receiving
NIC if can't buffer enough until OS has time to empty the buffers or they
get lost between kernel and userspace socket if backlog is too small.

My test environment was 4core AMD APU with Gbit NIC to Marvell Kirkwood
SheevaPlug with Gbit NIC. About first 100 lines get through, then lines
start getting lost.

drivers/net/netconsole.c | 3 ++-
include/linux/console.h | 1 +
kernel/printk/printk.c | 12 +++++++++---
3 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index a96cd8e..241c70f 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -661,7 +661,8 @@ static struct config_item *make_netconsole_target(struct config_group *group,
mutex_init(&nt->mutex);
memset(nt->np.remote_mac, 0xff, ETH_ALEN);
snprintf(nt->console.name, sizeof(nt->console.name), "netcon-%s", name);
- nt->console.flags = CON_ENABLED;
+ /* Dump existing printks when we register */
+ nt->console.flags = CON_ENABLED | CON_PRINTBUFFER | CON_PRINTALL;
nt->console.write = write_msg;
nt->console.loglevel = 0;

diff --git a/include/linux/console.h b/include/linux/console.h
index f3a8996..6f53a54 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -116,6 +116,7 @@ static inline int con_debug_leave(void)
#define CON_ANYTIME (16) /* Safe to call when cpu is offline */
#define CON_BRL (32) /* Used for a braille device */
#define CON_LOGLEVEL (64) /* Per-console log-level filtering */
+#define CON_PRINTALL (128) /* Extends CON_PRINTBUFFER */

struct console {
char name[16];
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 8f09f30..193665d 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2554,9 +2554,15 @@ void register_console(struct console *newcon)
* for us.
*/
raw_spin_lock_irqsave(&logbuf_lock, flags);
- console_seq = syslog_seq;
- console_idx = syslog_idx;
- console_prev = syslog_prev;
+ if (newcon->flags & CON_PRINTALL) {
+ console_seq = log_first_seq;
+ console_idx = log_first_idx;
+ console_prev = LOG_NEWLINE;
+ } else {
+ console_seq = syslog_seq;
+ console_idx = syslog_idx;
+ console_prev = syslog_prev;
+ }
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
/*
* We're about to replay the log buffer. Only do this to the
--
2.0.4