Skip to contentSkip to conversation

netconsole: implement extended console support

PATCH Superseded Tejun Heo 3 participants 11 messages May 1, 2015 – May 10, 2015

Subsystems (4)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

PATCH 1/1 netconsole: implement extended console support

This patchset is v2 of netconsole extended console support. v1 was part of "printk, netconsole: implement reliable netconsole" patchset[1]. The printk part is broken off to a separate patchset[2] "printk: implement extended console support" which this patchset is dependant upon.

Changes from the last last posting are

* Dynamic ext console de-registration is dropped. This made most of lock restructuring and refactoring in enable/disable path unnecessary. Ext netconsole is now registered on first use and stay registered. While this means that ext console support will stay enabled even after a dynamic extended console is disabled, such scenarios are likely very rare and the incurred overhead isn't drastic enough to justify the complexity.

* Retransmission handling is removed from the patchset. Handling retransmission in kernel doesn't provide enough benefits and is moved to userland.

netconsole emits one or more udp messages per each log message and only transmits the body, which works fine when it's used as a debugging tool on local network; however, netconsole, due to its advantages for troubleshooting kernel issues, is also used as a mechanism to collect kernel messages at larger scale where the packets may have to travel across congested networks or networks with multiple paths.

Of the handful large cluster setups that I've seen, two were using netconsole for fleet-wide kernel logging and having problem with lost messages. One was a HPC cluster which had a dedicated slower management network which was used for all management traffic where packet losses were fairly common for several different reasons - the network itself could get fairly overloaded at times and IPMI sharing the interface didn't seem to help either. The other is a large web service cluster where the aggregator is some hops away and packet losses do happen from time to time.

Because netconsole packets don't carry any metadata, it's impossible to tell what happened to the messages during transit and even combining it with messages transmitted via a separate reliable mechanism is challenging as it boils down to matching message content textually.

The "printk, netconsole: implement reliable netconsole" patchset[1] implements extended console support. If a console driver sets CON_EXTENDED, printk formats each message in the same way /dev/kmsg messages are formatted which includes all metadata and, for structured log messages, KEY=VALUE dictionary.

This patchset implements extended console support for netconsole, which allows log consumers access to complete log information and to tell which messages are missing and/or reordered, which can be used to implement reliable kernel message logging when combined with userland helpers.

Changes to netconsole are straight-forward. It optionally registers a separate extended console driver. printk passes in extended format messages which are transmitted the same way. The only complication is when the message is longer than the maximum payload size (1k). As each message should have proper header and the log receiver should be able to tell which part the fragment is, netconsole duplicates full header on each fragment and also adds an extra ncfrag=OFF/LEN header.

0001-netconsole-make-netconsole_target-enabled-a-bool.patch 0002-netconsole-make-all-dynamic-netconsoles-share-a-mute.patch 0003-netconsole-implement-extended-console-support.patch

David, the patchset is small enough that I don't think splitting it makes much sense. While the first two patches are mostly independent cleanups, they can be ignored w/o the third patch.

diffstat follows. Thanks.

 Documentation/networking/netconsole.txt |   34 ++++++
 drivers/net/netconsole.c                |  175 +++++++++++++++++++++++++++++---
 2 files changed, 192 insertions(+), 17 deletions(-)

--
tejun

[1] http://lkml.kernel.org/g/[email protected]
[2] http://lkml.kernel.org/g/[email protected]

author
#1 lore raw

PATCH 1/3 netconsole: make netconsole_target->enabled a bool

netconsole uses both bool and int for boolean values. Let's convert nt->enabled to bool for consistency.

Cc David Miller <[email protected]>
---
 drivers/net/netconsole.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/net/netconsole.c +9 -9
index 15731d1..09d4e12 100644
@@ -104,7 +104,7 @@ struct netconsole_target {
 #ifdef	CONFIG_NETCONSOLE_DYNAMIC
 	struct config_item	item;
 #endif
-	int			enabled;
+	bool			enabled;
 	struct mutex		mutex;
 	struct netpoll		np;
 };
@@ -197,7 +197,7 @@ static struct netconsole_target *alloc_param_target(char *target_config)
 	if (err)
 		goto fail;
 
-	nt->enabled = 1;
+	nt->enabled = true;
 
 	return nt;
 
@@ -322,13 +322,13 @@ static ssize_t store_enabled(struct netconsole_target *nt,
 		return err;
 	if (enabled < 0 || enabled > 1)
 		return -EINVAL;
-	if (enabled == nt->enabled) {
+	if ((bool)enabled == nt->enabled) {
 		pr_info("network logging has already %s\n",
 			nt->enabled ? "started" : "stopped");
 		return -EINVAL;
 	}
 
-	if (enabled) {	/* 1 */
+	if (enabled) {	/* true */
 		/*
 		 * Skip netpoll_parse_options() -- all the attributes are
 		 * already configured via configfs. Just print them out.
@@ -340,13 +340,13 @@ static ssize_t store_enabled(struct netconsole_target *nt,
 			return err;
 
 		pr_info("netconsole: network logging started\n");
-	} else {	/* 0 */
+	} else {	/* false */
 		/* We need to disable the netconsole before cleaning it up
 		 * otherwise we might end up in write_msg() with
-		 * nt->np.dev == NULL and nt->enabled == 1
+		 * nt->np.dev == NULL and nt->enabled == true
 		 */
 		spin_lock_irqsave(&target_list_lock, flags);
-		nt->enabled = 0;
+		nt->enabled = false;
 		spin_unlock_irqrestore(&target_list_lock, flags);
 		netpoll_cleanup(&nt->np);
 	}
@@ -594,7 +594,7 @@ static struct config_item *make_netconsole_target(struct config_group *group,
 
 	/*
 	 * Allocate and initialize with defaults.
-	 * Target is disabled at creation (enabled == 0).
+	 * Target is disabled at creation (!enabled).
 	 */
 	nt = kzalloc(sizeof(*nt), GFP_KERNEL);
 	if (!nt)
@@ -695,7 +695,7 @@ restart:
 				spin_lock_irqsave(&target_list_lock, flags);
 				dev_put(nt->np.dev);
 				nt->np.dev = NULL;
-				nt->enabled = 0;
+				nt->enabled = false;
 				stopped = true;
 				netconsole_target_put(nt);
 				goto restart;
Signature · 1 line
2.1.0
author
#2 lore raw

PATCH 2/3 netconsole: make all dynamic netconsoles share a mutex

In reply to Tejun Heo

Currently, each dynamic netconsole_target uses its own separate mutex to synchronize the configuration operations. This patch replaces the per-netconsole_target mutexes with a single mutex - dynamic_netconsole_mutex. The reduced granularity doesn't hurt anything, the code is minutely simpler and this'd allow adding operations which should be synchronized across all dynamic netconsoles.

Cc David Miller <[email protected]>
---
 drivers/net/netconsole.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/net/netconsole.c +3 -5
index 09d4e12..d9f0f85 100644
@@ -105,13 +105,13 @@ struct netconsole_target {
 	struct config_item	item;
 #endif
 	bool			enabled;
-	struct mutex		mutex;
 	struct netpoll		np;
 };
 
 #ifdef	CONFIG_NETCONSOLE_DYNAMIC
 
 static struct configfs_subsystem netconsole_subsys;
+static DEFINE_MUTEX(dynamic_netconsole_mutex);
 
 static int __init dynamic_netconsole_init(void)
 {
@@ -185,7 +185,6 @@ static struct netconsole_target *alloc_param_target(char *target_config)
 	strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
 	nt->np.local_port = 6665;
 	nt->np.remote_port = 6666;
-	mutex_init(&nt->mutex);
 	eth_broadcast_addr(nt->np.remote_mac);
 
 	/* Parse parameters and setup netpoll */
@@ -562,10 +561,10 @@ static ssize_t netconsole_target_attr_store(struct config_item *item,
 	struct netconsole_target_attr *na =
 		container_of(attr, struct netconsole_target_attr, attr);
 
-	mutex_lock(&nt->mutex);
+	mutex_lock(&dynamic_netconsole_mutex);
 	if (na->store)
 		ret = na->store(nt, buf, count);
-	mutex_unlock(&nt->mutex);
+	mutex_unlock(&dynamic_netconsole_mutex);
 
 	return ret;
 }
@@ -604,7 +603,6 @@ static struct config_item *make_netconsole_target(struct config_group *group,
 	strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
 	nt->np.local_port = 6665;
 	nt->np.remote_port = 6666;
-	mutex_init(&nt->mutex);
 	eth_broadcast_addr(nt->np.remote_mac);
 
 	/* Initialize the config_item member */
Signature · 1 line
2.1.0
author
#3 lore raw

PATCH 3/3 netconsole: implement extended console support

In reply to Tejun Heo

printk logbuf keeps various metadata and optional key=value dictionary for structured messages, both of which are stripped when messages are handed to regular console drivers.

It can be useful to have this metadata and dictionary available to netconsole consumers. This obviously makes logging via netconsole more complete and the sequence number in particular is useful in environments where messages may be lost or reordered in transit - e.g. when netconsole is used to collect messages in a large cluster where packets may have to travel congested hops to reach the aggregator. The lost and reordered messages can easily be identified and handled accordingly using the sequence numbers.

printk recently added extended console support which can be selected by setting CON_EXTENDED flag. From console driver side, not much changes. The only difference is that the text passed to the write callback is formatted the same way as /dev/kmsg.

This patch implements extended console support for netconsole which can be enabled by either prepending "+" to a netconsole boot param entry or echoing 1 to "extended" file in configfs. When enabled, netconsole transmits extended log messages with headers identical to /dev/kmsg output.

There's one complication due to message fragments. netconsole limits the maximum message size to 1k and messages longer than that are split into multiple fragments. As all extended console messages should carry matching headers and be uniquely identifiable, each extended message fragment carries full copy of the metadata and an extra header field to identify the specific fragment. The optional header is of the form "ncfrag=OFF/LEN" where OFF is the byte offset into the message body and LEN is the total length.

To avoid unnecessarily making printk format extended messages, Extended netconsole is registered with printk when the first extended netconsole is configured.

v2: Dropped dynamic unregistration of extended console driver, which
added complexity while not being too beneficial given that most netconsole configurations are static. ncfrag updated to use just byte offset and message length.

Cc David Miller <[email protected]>
---
 Documentation/networking/netconsole.txt |  34 +++++++-
 drivers/net/netconsole.c                | 149 +++++++++++++++++++++++++++++++-
 2 files changed, 180 insertions(+), 3 deletions(-)
Documentation/networking/netconsole.txt +33 -1
index a5d574a..76c2640 100644
@@ -2,6 +2,7 @@
 started by Ingo Molnar <[email protected]>, 2001.09.17
 2.6 port and netpoll api by Matt Mackall <[email protected]>, Sep 9 2003
 IPv6 support by Cong Wang <[email protected]>, Jan 1 2013
+Extended console support by Tejun Heo <[email protected]>, May 1 2015
 
 Please send bug reports to Matt Mackall <[email protected]>
 Satyam Sharma <[email protected]>, and Cong Wang <[email protected]>
@@ -24,9 +25,10 @@ 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]
 
    where
+        +             if present, enable extended console support
         src-port      source for UDP packets (defaults to 6665)
         src-ip        source IP to use (interface address)
         dev           network interface (eth0)
@@ -107,6 +109,7 @@ To remove a target:
 The interface exposes these parameters of a netconsole target to userspace:
 
 	enabled		Is this target currently enabled?	(read-write)
+	extended	Extended mode enabled			(read-write)
 	dev_name	Local network interface name		(read-write)
 	local_port	Source UDP port to use			(read-write)
 	remote_port	Remote agent's UDP port			(read-write)
@@ -132,6 +135,35 @@ You can also update the local interface dynamically. This is especially
 useful if you want to use interfaces that have newly come up (and may not
 have existed when netconsole was loaded / initialized).
 
+Extended console:
+=================
+
+If '+' is prefixed to the configuration line or "extended" config file
+is set to 1, extended console support is enabled. An example boot
+param follows.
+
+ linux [email protected]/eth1,[email protected]/12:34:56:78:9a:bc
+
+Log messages are transmitted with extended metadata header in the
+following format which is the same as /dev/kmsg.
+
+ <level>,<sequnum>,<timestamp>,<contflag>;<message text>
+
+Non printable characters in <message text> are escaped using "\xff"
+notation. If the message contains optional dictionary, verbatim
+newline is used as the delimeter.
+
+If a message doesn't fit in 1000 bytes, the message is split into
+multiple fragments by netconsole. These fragments are transmitted with
+"ncfrag" header field added.
+
+ ncfrag=<byte-offset>/<total-bytes>
+
+For example,
+
+ 6,416,1758426,-,ncfrag=0/33;the first chunk,
+ 6,416,1758426,-,ncfrag=16/33;the second chunk.
+
 Miscellaneous notes:
 ====================
 
drivers/net/netconsole.c +147 -2

233 lines not on this page · raw message

Signature · 1 line
2.1.0
#4 lore raw

Documentation/networking/netconsole.txt You can also update the local interface dynamically. This is especially · line 166

Quoted 11 lines · Tejun Heo
Documentation/networking/netconsole.txt +11 -0
+If a message doesn't fit in 1000 bytes, the message is split into
+multiple fragments by netconsole. These fragments are transmitted with
+"ncfrag" header field added.
+
+ ncfrag=<byte-offset>/<total-bytes>
+
+For example,
+
+ 6,416,1758426,-,ncfrag=0/33;the first chunk,
+ 6,416,1758426,-,ncfrag=16/33;the second chunk.
+

Wouldn't total-bytes > 1000 than 33 in this example?

Quoted 26 lines
drivers/net/netconsole.c +26 -0
+/**
+ * send_ext_msg_udp - send extended log message to target
+ * @nt: target to send message to
+ * @msg: extended log message to send
+ * @msg_len: length of message
+ *
+ * Transfer extended log @msg to @nt.  If @msg is longer than
+ * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with
+ * ncfrag header field added to identify them.
+ */
+static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
+			     int msg_len)
+{
+	static char buf[MAX_PRINT_CHUNK];
+	const int max_extra_len = sizeof(",ncfrag=0000/0000");
+	const char *header, *body;
+	int header_len = msg_len, body_len = 0;
+	int chunk_len, nr_chunks, i;
+
+	if (msg_len <= MAX_PRINT_CHUNK) {
+		netpoll_send_udp(&nt->np, msg, msg_len);
+		return;
+	}
+
+	/* need to insert extra header fields, detect header and body */
+	header = msg;

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 813

Quoted 10 lines
drivers/net/netconsole.c +10 -0
+	body = memchr(msg, ';', msg_len);
+	if (body) {
+		header_len = body - header;
+		body_len = msg_len - header_len - 1;
+		body++;
+	}
+
+	chunk_len = MAX_PRINT_CHUNK - header_len - max_extra_len;
+	if (WARN_ON_ONCE(chunk_len <= 0))
+		return;

This path is executed only when msg_len > MAX_PRINT_CHUNK. And since header_len == msg_len if body == NULL, chunk_len <= 0 is true. We will hit this WARN_ON_ONCE() if memchr(msg, ';', msg_len) == NULL which will fail to send the message. Is this what you want?

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 856

Quoted 11 lines
drivers/net/netconsole.c +11 -0
+static void write_ext_msg(struct console *con, const char *msg,
+			  unsigned int len)
+{
+	struct netconsole_target *nt;
+	unsigned long flags;
+
+	if ((oops_only && !oops_in_progress) || list_empty(&target_list))
+		return;
+
+	spin_lock_irqsave(&target_list_lock, flags);
+	list_for_each_entry(nt, &target_list, list)

Don't you need to call netconsole_target_get() here

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 858

Quoted 2 lines
drivers/net/netconsole.c +2 -0
+		if (nt->extended && nt->enabled && netif_running(nt->np.dev))
+			send_ext_msg_udp(nt, msg, len);

and netconsole_target_put() here as with write_msg()?

Quoted 3 lines
drivers/net/netconsole.c +3 -0
+	spin_unlock_irqrestore(&target_list_lock, flags);
+}
+
author
#5 lore raw

Hello, Tetsuo.

Quoted 8 lines · Tetsuo Handa

Documentation/networking/netconsole.txt You can also update the local interface dynamically. This is especially · line 166

Quoted 5 lines · Tejun Heo
Documentation/networking/netconsole.txt +5 -0
+For example,
+
+ 6,416,1758426,-,ncfrag=0/33;the first chunk,
+ 6,416,1758426,-,ncfrag=16/33;the second chunk.
+

Wouldn't total-bytes > 1000 than 33 in this example?

It's trying to give an example on how the ncfrag header looks like but yeah I'll tweak it a bit and add further explanation.

Quoted 33 lines
Quoted 33 lines
drivers/net/netconsole.c +33 -0
+/**
+ * send_ext_msg_udp - send extended log message to target
+ * @nt: target to send message to
+ * @msg: extended log message to send
+ * @msg_len: length of message
+ *
+ * Transfer extended log @msg to @nt.  If @msg is longer than
+ * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with
+ * ncfrag header field added to identify them.
+ */
+static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
+			     int msg_len)
+{
+	static char buf[MAX_PRINT_CHUNK];
+	const int max_extra_len = sizeof(",ncfrag=0000/0000");
+	const char *header, *body;
+	int header_len = msg_len, body_len = 0;
+	int chunk_len, nr_chunks, i;
+
+	if (msg_len <= MAX_PRINT_CHUNK) {
+		netpoll_send_udp(&nt->np, msg, msg_len);
+		return;
+	}
+
+	/* need to insert extra header fields, detect header and body */
+	header = msg;
+	body = memchr(msg, ';', msg_len);
+	if (body) {
+		header_len = body - header;
+		body_len = msg_len - header_len - 1;
+		body++;
+	}
+
Quoted 8 lines

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 813

Quoted 3 lines
drivers/net/netconsole.c +3 -0
+	chunk_len = MAX_PRINT_CHUNK - header_len - max_extra_len;
+	if (WARN_ON_ONCE(chunk_len <= 0))
+		return;

This path is executed only when msg_len > MAX_PRINT_CHUNK. And since header_len == msg_len if body == NULL, chunk_len <= 0 is true. We will hit this WARN_ON_ONCE() if memchr(msg, ';', msg_len) == NULL which will fail to send the message. Is this what you want?

Yeah, extended consoles should be fed messages w/ proper headers. If not, we warn and bail.

Quoted 14 lines

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 856

Quoted 11 lines
drivers/net/netconsole.c +11 -0
+static void write_ext_msg(struct console *con, const char *msg,
+			  unsigned int len)
+{
+	struct netconsole_target *nt;
+	unsigned long flags;
+
+	if ((oops_only && !oops_in_progress) || list_empty(&target_list))
+		return;
+
+	spin_lock_irqsave(&target_list_lock, flags);
+	list_for_each_entry(nt, &target_list, list)

Don't you need to call netconsole_target_get() here

Quoted 4 lines

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 858

Quoted 2 lines
drivers/net/netconsole.c +2 -0
+		if (nt->extended && nt->enabled && netif_running(nt->np.dev))
+			send_ext_msg_udp(nt, msg, len);

and netconsole_target_put() here as with write_msg()?

Ah, I dropped the patch which removed it from write_msg() but the following pattern doesn't do anything. It's just pure confusion.

	lock;
	ref++ on an item on the index;
	do something w/o releasing lock;
	ref-- on the item;
	unlock;

netconsole_netdev_event() has to release lock to process the item so refcnting is needed there. It got duplicated in write_msg() for no reason. I'll add a patch to remove it.

Thanks.

Signature · 1 line
tejun
author
#6 lore raw

PATCH v3 3/3 netconsole: implement extended console support

In reply to Tejun Heo
Quoted 1 line

From f9530ac244c12a89837736269a1930291a360875 Mon Sep 17 00:00:00 2001

From: Tejun Heo <[email protected]>
Date: Mon, 4 May 2015 15:57:54 -0400

printk logbuf keeps various metadata and optional key=value dictionary for structured messages, both of which are stripped when messages are handed to regular console drivers.

It can be useful to have this metadata and dictionary available to netconsole consumers. This obviously makes logging via netconsole more complete and the sequence number in particular is useful in environments where messages may be lost or reordered in transit - e.g. when netconsole is used to collect messages in a large cluster where packets may have to travel congested hops to reach the aggregator. The lost and reordered messages can easily be identified and handled accordingly using the sequence numbers.

printk recently added extended console support which can be selected by setting CON_EXTENDED flag. From console driver side, not much changes. The only difference is that the text passed to the write callback is formatted the same way as /dev/kmsg.

This patch implements extended console support for netconsole which can be enabled by either prepending "+" to a netconsole boot param entry or echoing 1 to "extended" file in configfs. When enabled, netconsole transmits extended log messages with headers identical to /dev/kmsg output.

There's one complication due to message fragments. netconsole limits the maximum message size to 1k and messages longer than that are split into multiple fragments. As all extended console messages should carry matching headers and be uniquely identifiable, each extended message fragment carries full copy of the metadata and an extra header field to identify the specific fragment. The optional header is of the form "ncfrag=OFF/LEN" where OFF is the byte offset into the message body and LEN is the total length.

To avoid unnecessarily making printk format extended messages, Extended netconsole is registered with printk when the first extended netconsole is configured.

v3: Tweaked documentation to make clarify that the example assumes a
lot smaller chunk size. Updated to apply on top of spurious target get/put removal in write_msg().

v2: Dropped dynamic unregistration of extended console driver, which
added complexity while not being too beneficial given that most netconsole configurations are static. ncfrag updated to use just byte offset and message length.

Cc Tetsuo Handa <[email protected]>
Cc David Miller <[email protected]>

---
Hello,

If anyone wants the whole series to be reposted, please let me know. The updated patchset is available in the following git branch.

git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.git review-netconsole-ext-console

Thanks.

 Documentation/networking/netconsole.txt |  35 +++++++-
 drivers/net/netconsole.c                | 149 +++++++++++++++++++++++++++++++-
 2 files changed, 181 insertions(+), 3 deletions(-)
Documentation/networking/netconsole.txt +34 -1
index a5d574a..30409a3 100644
@@ -2,6 +2,7 @@
 started by Ingo Molnar <[email protected]>, 2001.09.17
 2.6 port and netpoll api by Matt Mackall <[email protected]>, Sep 9 2003
 IPv6 support by Cong Wang <[email protected]>, Jan 1 2013
+Extended console support by Tejun Heo <[email protected]>, May 1 2015
 
 Please send bug reports to Matt Mackall <[email protected]>
 Satyam Sharma <[email protected]>, and Cong Wang <[email protected]>
@@ -24,9 +25,10 @@ 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]
 
    where
+        +             if present, enable extended console support
         src-port      source for UDP packets (defaults to 6665)
         src-ip        source IP to use (interface address)
         dev           network interface (eth0)
@@ -107,6 +109,7 @@ To remove a target:
 The interface exposes these parameters of a netconsole target to userspace:
 
 	enabled		Is this target currently enabled?	(read-write)
+	extended	Extended mode enabled			(read-write)
 	dev_name	Local network interface name		(read-write)
 	local_port	Source UDP port to use			(read-write)
 	remote_port	Remote agent's UDP port			(read-write)
@@ -132,6 +135,36 @@ You can also update the local interface dynamically. This is especially
 useful if you want to use interfaces that have newly come up (and may not
 have existed when netconsole was loaded / initialized).
 
+Extended console:
+=================
+
+If '+' is prefixed to the configuration line or "extended" config file
+is set to 1, extended console support is enabled. An example boot
+param follows.
+
+ linux [email protected]/eth1,[email protected]/12:34:56:78:9a:bc
+
+Log messages are transmitted with extended metadata header in the
+following format which is the same as /dev/kmsg.
+
+ <level>,<sequnum>,<timestamp>,<contflag>;<message text>
+
+Non printable characters in <message text> are escaped using "\xff"
+notation. If the message contains optional dictionary, verbatim
+newline is used as the delimeter.
+
+If a message doesn't fit in certain number of bytes (currently 1000),
+the message is split into multiple fragments by netconsole. These
+fragments are transmitted with "ncfrag" header field added.
+
+ ncfrag=<byte-offset>/<total-bytes>
+
+For example, assuming a lot smaller chunk size, a message "the first
+chunk, the 2nd chunk." may be split as follows.
+
+ 6,416,1758426,-,ncfrag=0/31;the first chunk,
+ 6,416,1758426,-,ncfrag=16/31; the 2nd chunk.
+
 Miscellaneous notes:
 ====================
 
drivers/net/netconsole.c +147 -2

233 lines not on this page · raw message

Signature · 1 line
2.1.0
#7 lore raw

Hi Tejun,

2015-05-04, 16:04:56 -0400, Tejun Heo wrote:

[...]

Quoted 5 lines
drivers/net/netconsole.c +5 -0
+/**
+ * send_ext_msg_udp - send extended log message to target
+ * @nt: target to send message to
+ * @msg: extended log message to send
+ * @msg_len: length of message

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 792

Quoted 10 lines
drivers/net/netconsole.c +10 -0
+ *
+ * Transfer extended log @msg to @nt.  If @msg is longer than
+ * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with
+ * ncfrag header field added to identify them.
+ */
+static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
+			     int msg_len)
+{
+	static char buf[MAX_PRINT_CHUNK];
+	const int max_extra_len = sizeof(",ncfrag=0000/0000");

Is msg_len guaranteed < 10000? Otherwise I think the WARN in the send loop can trigger.

Also, I think your count is correct because sizeof adds one to the string's length, but you don't explicitly account for the ';' between header and body fragment here (and in chunk_len). header_len will stop before the ;.

Quoted 20 lines
drivers/net/netconsole.c +20 -0
+	const char *header, *body;
+	int header_len = msg_len, body_len = 0;
+	int chunk_len, nr_chunks, i;
+
+	if (msg_len <= MAX_PRINT_CHUNK) {
+		netpoll_send_udp(&nt->np, msg, msg_len);
+		return;
+	}
+
+	/* need to insert extra header fields, detect header and body */
+	header = msg;
+	body = memchr(msg, ';', msg_len);
+	if (body) {
+		header_len = body - header;
+		body_len = msg_len - header_len - 1;
+		body++;
+	}
+
+	chunk_len = MAX_PRINT_CHUNK - header_len - max_extra_len;
+	if (WARN_ON_ONCE(chunk_len <= 0))

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 822

Quoted 10 lines
drivers/net/netconsole.c +10 -0
+		return;
+
+	/*
+	 * Transfer possibly multiple chunks with extra header fields.
+	 *
+	 * If @msg needs to be split to fit MAX_PRINT_CHUNK, add
+	 * "ncfrag=<byte-offset>/<total-bytes>" to identify each chunk.
+	 */
+	memcpy(buf, header, header_len);
+	nr_chunks = DIV_ROUND_UP(body_len, chunk_len);

Wouldn't it be simpler to loop on the remaining size, instead of doing a division?

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 829

Quoted 7 lines
drivers/net/netconsole.c +7 -0
+
+	for (i = 0; i < nr_chunks; i++) {
+		int offset = i * chunk_len;
+		int this_header = header_len;
+		int this_chunk = min(body_len - offset, chunk_len);
+
+		if (nr_chunks > 1)

We already know that there will be more than one chunk, since you handle msg_len <= MAX_PRINT_CHUNK at the beginning?

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 836

Quoted 7 lines
drivers/net/netconsole.c +7 -0
+			this_header += scnprintf(buf + this_header,
+						 sizeof(buf) - this_header,
+						 ",ncfrag=%d/%d;",
+						 offset, body_len);
+
+		if (WARN_ON_ONCE(this_header + chunk_len > MAX_PRINT_CHUNK))
+			return;

This WARN doesn't really seem necessary to me, except for the msg_len maximum I mentionned earlier.
And if we don't use nr_chunks, we could compute the fragment's length here in case some computation went wrong.

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 841

Quoted 5 lines
drivers/net/netconsole.c +5 -0
+
+		memcpy(buf + this_header, body, this_chunk);
+
+		netpoll_send_udp(&nt->np, buf, this_header + this_chunk);
+

netpoll_send_udp already does a memcpy (in skb_copy_to_linear_data). Maybe it would be better to modify netpoll_send_udp, or add a variant that takes two buffers? or more than two, with something like an iovec?

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = {

Quoted 5 lines
drivers/net/netconsole.c +3 -0
+		body += this_chunk;
+	}
+}
 
[...]

Thanks,

Signature · 1 line
Sabrina
author
#8 lore raw

Hello, Sabrina.

Quoted 8 lines · Sabrina Dubroca

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 792

Quoted 5 lines
drivers/net/netconsole.c +5 -0
+static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
+			     int msg_len)
+{
+	static char buf[MAX_PRINT_CHUNK];
+	const int max_extra_len = sizeof(",ncfrag=0000/0000");

Is msg_len guaranteed < 10000? Otherwise I think the WARN in the send loop can trigger.

Yeap, the absolute maximum is 8k.

Quoted 4 lines

Also, I think your count is correct because sizeof adds one to the string's length, but you don't explicitly account for the ';' between header and body fragment here (and in chunk_len). header_len will stop before the ;.

Hmm... ';' would be accounted for in the length of the original message. This function just needs to count the extra number of bytes to be added which means that sizeof() - 1 would be the precise count here. Do we care?

Quoted 11 lines

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 822

Quoted 8 lines
drivers/net/netconsole.c +8 -0
+	/*
+	 * Transfer possibly multiple chunks with extra header fields.
+	 *
+	 * If @msg needs to be split to fit MAX_PRINT_CHUNK, add
+	 * "ncfrag=<byte-offset>/<total-bytes>" to identify each chunk.
+	 */
+	memcpy(buf, header, header_len);
+	nr_chunks = DIV_ROUND_UP(body_len, chunk_len);

Wouldn't it be simpler to loop on the remaining size, instead of doing a division?

Indeed, this is a remnant of earlier code which emitted total number of chunks. Will restructure.

Quoted 10 lines

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 829

Quoted 7 lines
drivers/net/netconsole.c +7 -0
+
+	for (i = 0; i < nr_chunks; i++) {
+		int offset = i * chunk_len;
+		int this_header = header_len;
+		int this_chunk = min(body_len - offset, chunk_len);
+
+		if (nr_chunks > 1)

We already know that there will be more than one chunk, since you handle msg_len <= MAX_PRINT_CHUNK at the beginning?

Ditto, earlier code had two types of conditions which trigger this code path. Will remove.

Quoted 4 lines
Quoted 4 lines
drivers/net/netconsole.c +4 -0
+			this_header += scnprintf(buf + this_header,
+						 sizeof(buf) - this_header,
+						 ",ncfrag=%d/%d;",
+						 offset, body_len);
Quoted 8 lines

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 836

Quoted 3 lines
drivers/net/netconsole.c +3 -0
+
+		if (WARN_ON_ONCE(this_header + chunk_len > MAX_PRINT_CHUNK))
+			return;

This WARN doesn't really seem necessary to me, except for the msg_len maximum I mentionned earlier.
And if we don't use nr_chunks, we could compute the fragment's length here in case some computation went wrong.

I think it'd be better to keep it tho. It's one unlikely branch in an already unlikely path. It doesn't really cost anything. Stuff happens, code changes and it'd suck if e.g. netconsole ends up causing memory corruption following changes / failures in upper layers.

Quoted 9 lines

drivers/net/netconsole.c static struct notifier_block netconsole_netdev_notifier = { · line 841

Quoted 5 lines
drivers/net/netconsole.c +5 -0
+
+		memcpy(buf + this_header, body, this_chunk);
+
+		netpoll_send_udp(&nt->np, buf, this_header + this_chunk);
+

netpoll_send_udp already does a memcpy (in skb_copy_to_linear_data). Maybe it would be better to modify netpoll_send_udp, or add a variant that takes two buffers? or more than two, with something like an iovec?

The splitting is a very rare event. I don't think we need to be worrying about its performance at this level.

Thanks.

Signature · 1 line
tejun
author
#9 lore raw

PATCH 1/1 netconsole: remove unnecessary netconsole_target_get/out() from write_msg()

In reply to Tejun Heo
Quoted 1 line

From 958d3e14720a35c6103668c69d58751b36053d69 Mon Sep 17 00:00:00 2001

From: Tejun Heo <[email protected]>
Date: Mon, 4 May 2015 15:57:54 -0400

write_msg() grabs target_list_lock and walks target_list invoking netpool_send_udp() on each target. Curiously, it protects each iteration with netconsole_target_get/put() even though it never releases target_list_lock which protects all the members.

While this doesn't harm anything, it doesn't serve any purpose either. The items on the list can't go away while target_list_lock is held. Remove the unnecessary get/put pair.

Cc David Miller <[email protected]>
Cc Tetsuo Handa <[email protected]>

---
Hello,

If anyone wants the whole series to be reposted, please let me know. The updated patchset is available in the following git branch.

git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.git review-netconsole-ext-console

Thanks.

 drivers/net/netconsole.c | 2 --
 1 file changed, 2 deletions(-)
drivers/net/netconsole.c +0 -2
index 15731d1..30c0524 100644
@@ -744,7 +744,6 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
 
 	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
@@ -760,7 +759,6 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
 				left -= frag;
 			}
 		}
-		netconsole_target_put(nt);
 	}
 	spin_unlock_irqrestore(&target_list_lock, flags);
 }
Signature · 1 line
2.1.0