[PATCH 12/16] netconsole: implement extended console support

2015-04-16 Thread Tejun Heo
netconsole transmits raw console messages using one or multiple UDP
packets and there's no way to find out whether the packets are lost in
transit or received out of order.  Depending on the setup, this can
make the logging significantly unreliable and untrustworthy.

With the new extended console support, printk now can be told to
expose log metadata including the message sequence number to console
drivers which can be used by log receivers to determine whether and
which messages are missing and reorder messages received out of order.

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.

netconsole may have to split a single messages to multiple fragments.
In this case, if the extended mode is enabled, an optional header of
the form "ncfrag=OFF@IDX/NR" is added to each fragment where OFF is
the byte offset of the message body, IDX is the 0-based fragment index
and NR is the number of total fragments for this message.

To avoid unnecessarily forcing printk to format extended messages,
extended netconsole is registered with printk iff it's actually used.

Signed-off-by: Tejun Heo 
Cc: David Miller 
---
 drivers/net/netconsole.c | 159 ++-
 1 file changed, 158 insertions(+), 1 deletion(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index d72d902..626d9f0 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -83,6 +83,10 @@ static LIST_HEAD(target_list);
 /* protects target creation/destruction and enable/disable */
 static DEFINE_MUTEX(netconsole_mutex);
 
+static bool netconsole_ext_used_during_init;
+static bool netconsole_ext_registered;
+static struct console netconsole_ext;
+
 static struct console netconsole;
 
 /**
@@ -112,6 +116,7 @@ struct netconsole_target {
 #endif
boolenabled;
booldisable_scheduled;
+   boolextended;
struct netpoll  np;
 };
 
@@ -194,6 +199,81 @@ static struct netconsole_target 
*alloc_netconsole_target(void)
return nt;
 }
 
+/**
+ * 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 too long, it'll be split
+ * and transmitted in multiple chunks with ncfrag header field added to
+ * enable correct reassembly.
+ */
+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=@00/00");
+   const char *header, *body;
+   int header_len = msg_len, body_len = 0;
+   int chunk_len, nr_chunks, i;
+
+   if (!nt->enabled || !netif_running(nt->np.dev))
+   return;
+
+   if (msg_len <= MAX_PRINT_CHUNK) {
+   netpoll_send_udp(>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))
+   return;
+
+   /*
+* Transfer possibly multiple chunks with extra header fields.
+*
+* If @msg needs to be split to fit MAX_PRINT_CHUNK, add
+* "ncfrag=@<0-based-chunk-index>/" to
+* enable proper reassembly on receiver side.
+*/
+   memcpy(buf, header, header_len);
+   nr_chunks = DIV_ROUND_UP(body_len, chunk_len);
+
+   for (i = 0; i < nr_chunks; i++) {
+   int this_header = header_len;
+   int this_chunk;
+
+   if (nr_chunks > 1)
+   this_header += scnprintf(buf + this_header,
+sizeof(buf) - this_header,
+",ncfrag=%d@%d/%d",
+i * chunk_len, i, nr_chunks);
+   if (this_header < sizeof(buf))
+   buf[this_header++] = ';';
+
+   if (WARN_ON_ONCE(this_header + chunk_len > MAX_PRINT_CHUNK))
+   return;
+
+   this_chunk = min(body_len, chunk_len);
+   memcpy(buf + this_header, body, this_chunk);
+
+   netpoll_send_udp(>np, buf, this_header + this_chunk);
+
+   body += this_chunk;
+   body_len -= this_chunk;
+   }
+}
+
 static int netconsole_enable(struct 

[PATCH 12/16] netconsole: implement extended console support

2015-04-16 Thread Tejun Heo
netconsole transmits raw console messages using one or multiple UDP
packets and there's no way to find out whether the packets are lost in
transit or received out of order.  Depending on the setup, this can
make the logging significantly unreliable and untrustworthy.

With the new extended console support, printk now can be told to
expose log metadata including the message sequence number to console
drivers which can be used by log receivers to determine whether and
which messages are missing and reorder messages received out of order.

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.

netconsole may have to split a single messages to multiple fragments.
In this case, if the extended mode is enabled, an optional header of
the form ncfrag=OFF@IDX/NR is added to each fragment where OFF is
the byte offset of the message body, IDX is the 0-based fragment index
and NR is the number of total fragments for this message.

To avoid unnecessarily forcing printk to format extended messages,
extended netconsole is registered with printk iff it's actually used.

Signed-off-by: Tejun Heo t...@kernel.org
Cc: David Miller da...@davemloft.net
---
 drivers/net/netconsole.c | 159 ++-
 1 file changed, 158 insertions(+), 1 deletion(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index d72d902..626d9f0 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -83,6 +83,10 @@ static LIST_HEAD(target_list);
 /* protects target creation/destruction and enable/disable */
 static DEFINE_MUTEX(netconsole_mutex);
 
+static bool netconsole_ext_used_during_init;
+static bool netconsole_ext_registered;
+static struct console netconsole_ext;
+
 static struct console netconsole;
 
 /**
@@ -112,6 +116,7 @@ struct netconsole_target {
 #endif
boolenabled;
booldisable_scheduled;
+   boolextended;
struct netpoll  np;
 };
 
@@ -194,6 +199,81 @@ static struct netconsole_target 
*alloc_netconsole_target(void)
return nt;
 }
 
+/**
+ * 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 too long, it'll be split
+ * and transmitted in multiple chunks with ncfrag header field added to
+ * enable correct reassembly.
+ */
+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=@00/00);
+   const char *header, *body;
+   int header_len = msg_len, body_len = 0;
+   int chunk_len, nr_chunks, i;
+
+   if (!nt-enabled || !netif_running(nt-np.dev))
+   return;
+
+   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))
+   return;
+
+   /*
+* Transfer possibly multiple chunks with extra header fields.
+*
+* If @msg needs to be split to fit MAX_PRINT_CHUNK, add
+* ncfrag=byte-offset@0-based-chunk-index/total-chunks to
+* enable proper reassembly on receiver side.
+*/
+   memcpy(buf, header, header_len);
+   nr_chunks = DIV_ROUND_UP(body_len, chunk_len);
+
+   for (i = 0; i  nr_chunks; i++) {
+   int this_header = header_len;
+   int this_chunk;
+
+   if (nr_chunks  1)
+   this_header += scnprintf(buf + this_header,
+sizeof(buf) - this_header,
+,ncfrag=%d@%d/%d,
+i * chunk_len, i, nr_chunks);
+   if (this_header  sizeof(buf))
+   buf[this_header++] = ';';
+
+   if (WARN_ON_ONCE(this_header + chunk_len  MAX_PRINT_CHUNK))
+   return;
+
+   this_chunk = min(body_len, chunk_len);
+   memcpy(buf + this_header, body, this_chunk);
+
+   netpoll_send_udp(nt-np, buf, this_header + this_chunk);
+
+   body += this_chunk;
+   body_len -= this_chunk;
+   }
+}