aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/netconsole.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2015-06-25 18:01:41 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-25 20:00:39 -0400
commite2f15f9a79201ddd596727b84a85c419ee57ad5c (patch)
tree95fade6317d2eb7603485fefc2268410b82b3eaf /drivers/net/netconsole.c
parent369e5a888179f31dcd7cc8e02d695e6481a66075 (diff)
netconsole: implement extended console support
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. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: David Miller <davem@davemloft.net> Cc: Kay Sievers <kay@vrfy.org> Cc: Petr Mladek <pmladek@suse.cz> Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r--drivers/net/netconsole.c141
1 files changed, 139 insertions, 2 deletions
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 9b0c81e7c752..97f3acd44798 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -79,6 +79,12 @@ static LIST_HEAD(target_list);
79/* This needs to be a spinlock because write_msg() cannot sleep */ 79/* This needs to be a spinlock because write_msg() cannot sleep */
80static DEFINE_SPINLOCK(target_list_lock); 80static DEFINE_SPINLOCK(target_list_lock);
81 81
82/*
83 * Console driver for extended netconsoles. Registered on the first use to
84 * avoid unnecessarily enabling ext message formatting.
85 */
86static struct console netconsole_ext;
87
82/** 88/**
83 * struct netconsole_target - Represents a configured netconsole target. 89 * struct netconsole_target - Represents a configured netconsole target.
84 * @list: Links this target into the target_list. 90 * @list: Links this target into the target_list.
@@ -105,6 +111,7 @@ struct netconsole_target {
105 struct config_item item; 111 struct config_item item;
106#endif 112#endif
107 bool enabled; 113 bool enabled;
114 bool extended;
108 struct netpoll np; 115 struct netpoll np;
109}; 116};
110 117
@@ -187,6 +194,11 @@ static struct netconsole_target *alloc_param_target(char *target_config)
187 nt->np.remote_port = 6666; 194 nt->np.remote_port = 6666;
188 eth_broadcast_addr(nt->np.remote_mac); 195 eth_broadcast_addr(nt->np.remote_mac);
189 196
197 if (*target_config == '+') {
198 nt->extended = true;
199 target_config++;
200 }
201
190 /* Parse parameters and setup netpoll */ 202 /* Parse parameters and setup netpoll */
191 err = netpoll_parse_options(&nt->np, target_config); 203 err = netpoll_parse_options(&nt->np, target_config);
192 if (err) 204 if (err)
@@ -257,6 +269,11 @@ static ssize_t show_enabled(struct netconsole_target *nt, char *buf)
257 return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled); 269 return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled);
258} 270}
259 271
272static ssize_t show_extended(struct netconsole_target *nt, char *buf)
273{
274 return snprintf(buf, PAGE_SIZE, "%d\n", nt->extended);
275}
276
260static ssize_t show_dev_name(struct netconsole_target *nt, char *buf) 277static ssize_t show_dev_name(struct netconsole_target *nt, char *buf)
261{ 278{
262 return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name); 279 return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name);
@@ -328,6 +345,11 @@ static ssize_t store_enabled(struct netconsole_target *nt,
328 } 345 }
329 346
330 if (enabled) { /* true */ 347 if (enabled) { /* true */
348 if (nt->extended && !(netconsole_ext.flags & CON_ENABLED)) {
349 netconsole_ext.flags |= CON_ENABLED;
350 register_console(&netconsole_ext);
351 }
352
331 /* 353 /*
332 * Skip netpoll_parse_options() -- all the attributes are 354 * Skip netpoll_parse_options() -- all the attributes are
333 * already configured via configfs. Just print them out. 355 * already configured via configfs. Just print them out.
@@ -355,6 +377,30 @@ static ssize_t store_enabled(struct netconsole_target *nt,
355 return strnlen(buf, count); 377 return strnlen(buf, count);
356} 378}
357 379
380static ssize_t store_extended(struct netconsole_target *nt,
381 const char *buf,
382 size_t count)
383{
384 int extended;
385 int err;
386
387 if (nt->enabled) {
388 pr_err("target (%s) is enabled, disable to update parameters\n",
389 config_item_name(&nt->item));
390 return -EINVAL;
391 }
392
393 err = kstrtoint(buf, 10, &extended);
394 if (err < 0)
395 return err;
396 if (extended < 0 || extended > 1)
397 return -EINVAL;
398
399 nt->extended = extended;
400
401 return strnlen(buf, count);
402}
403
358static ssize_t store_dev_name(struct netconsole_target *nt, 404static ssize_t store_dev_name(struct netconsole_target *nt,
359 const char *buf, 405 const char *buf,
360 size_t count) 406 size_t count)
@@ -507,6 +553,7 @@ static struct netconsole_target_attr netconsole_target_##_name = \
507 __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name) 553 __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name)
508 554
509NETCONSOLE_TARGET_ATTR_RW(enabled); 555NETCONSOLE_TARGET_ATTR_RW(enabled);
556NETCONSOLE_TARGET_ATTR_RW(extended);
510NETCONSOLE_TARGET_ATTR_RW(dev_name); 557NETCONSOLE_TARGET_ATTR_RW(dev_name);
511NETCONSOLE_TARGET_ATTR_RW(local_port); 558NETCONSOLE_TARGET_ATTR_RW(local_port);
512NETCONSOLE_TARGET_ATTR_RW(remote_port); 559NETCONSOLE_TARGET_ATTR_RW(remote_port);
@@ -517,6 +564,7 @@ NETCONSOLE_TARGET_ATTR_RW(remote_mac);
517 564
518static struct configfs_attribute *netconsole_target_attrs[] = { 565static struct configfs_attribute *netconsole_target_attrs[] = {
519 &netconsole_target_enabled.attr, 566 &netconsole_target_enabled.attr,
567 &netconsole_target_extended.attr,
520 &netconsole_target_dev_name.attr, 568 &netconsole_target_dev_name.attr,
521 &netconsole_target_local_port.attr, 569 &netconsole_target_local_port.attr,
522 &netconsole_target_remote_port.attr, 570 &netconsole_target_remote_port.attr,
@@ -727,6 +775,82 @@ static struct notifier_block netconsole_netdev_notifier = {
727 .notifier_call = netconsole_netdev_event, 775 .notifier_call = netconsole_netdev_event,
728}; 776};
729 777
778/**
779 * send_ext_msg_udp - send extended log message to target
780 * @nt: target to send message to
781 * @msg: extended log message to send
782 * @msg_len: length of message
783 *
784 * Transfer extended log @msg to @nt. If @msg is longer than
785 * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with
786 * ncfrag header field added to identify them.
787 */
788static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
789 int msg_len)
790{
791 static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */
792 const char *header, *body;
793 int offset = 0;
794 int header_len, body_len;
795
796 if (msg_len <= MAX_PRINT_CHUNK) {
797 netpoll_send_udp(&nt->np, msg, msg_len);
798 return;
799 }
800
801 /* need to insert extra header fields, detect header and body */
802 header = msg;
803 body = memchr(msg, ';', msg_len);
804 if (WARN_ON_ONCE(!body))
805 return;
806
807 header_len = body - header;
808 body_len = msg_len - header_len - 1;
809 body++;
810
811 /*
812 * Transfer multiple chunks with the following extra header.
813 * "ncfrag=<byte-offset>/<total-bytes>"
814 */
815 memcpy(buf, header, header_len);
816
817 while (offset < body_len) {
818 int this_header = header_len;
819 int this_chunk;
820
821 this_header += scnprintf(buf + this_header,
822 sizeof(buf) - this_header,
823 ",ncfrag=%d/%d;", offset, body_len);
824
825 this_chunk = min(body_len - offset,
826 MAX_PRINT_CHUNK - this_header);
827 if (WARN_ON_ONCE(this_chunk <= 0))
828 return;
829
830 memcpy(buf + this_header, body + offset, this_chunk);
831
832 netpoll_send_udp(&nt->np, buf, this_header + this_chunk);
833
834 offset += this_chunk;
835 }
836}
837
838static void write_ext_msg(struct console *con, const char *msg,
839 unsigned int len)
840{
841 struct netconsole_target *nt;
842 unsigned long flags;
843
844 if ((oops_only && !oops_in_progress) || list_empty(&target_list))
845 return;
846
847 spin_lock_irqsave(&target_list_lock, flags);
848 list_for_each_entry(nt, &target_list, list)
849 if (nt->extended && nt->enabled && netif_running(nt->np.dev))
850 send_ext_msg_udp(nt, msg, len);
851 spin_unlock_irqrestore(&target_list_lock, flags);
852}
853
730static void write_msg(struct console *con, const char *msg, unsigned int len) 854static void write_msg(struct console *con, const char *msg, unsigned int len)
731{ 855{
732 int frag, left; 856 int frag, left;
@@ -742,7 +866,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
742 866
743 spin_lock_irqsave(&target_list_lock, flags); 867 spin_lock_irqsave(&target_list_lock, flags);
744 list_for_each_entry(nt, &target_list, list) { 868 list_for_each_entry(nt, &target_list, list) {
745 if (nt->enabled && netif_running(nt->np.dev)) { 869 if (!nt->extended && nt->enabled && netif_running(nt->np.dev)) {
746 /* 870 /*
747 * We nest this inside the for-each-target loop above 871 * We nest this inside the for-each-target loop above
748 * so that we're able to get as much logging out to 872 * so that we're able to get as much logging out to
@@ -761,6 +885,12 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
761 spin_unlock_irqrestore(&target_list_lock, flags); 885 spin_unlock_irqrestore(&target_list_lock, flags);
762} 886}
763 887
888static struct console netconsole_ext = {
889 .name = "netcon_ext",
890 .flags = CON_EXTENDED, /* starts disabled, registered on first use */
891 .write = write_ext_msg,
892};
893
764static struct console netconsole = { 894static struct console netconsole = {
765 .name = "netcon", 895 .name = "netcon",
766 .flags = CON_ENABLED, 896 .flags = CON_ENABLED,
@@ -783,7 +913,11 @@ static int __init init_netconsole(void)
783 goto fail; 913 goto fail;
784 } 914 }
785 /* Dump existing printks when we register */ 915 /* Dump existing printks when we register */
786 netconsole.flags |= CON_PRINTBUFFER; 916 if (nt->extended)
917 netconsole_ext.flags |= CON_PRINTBUFFER |
918 CON_ENABLED;
919 else
920 netconsole.flags |= CON_PRINTBUFFER;
787 921
788 spin_lock_irqsave(&target_list_lock, flags); 922 spin_lock_irqsave(&target_list_lock, flags);
789 list_add(&nt->list, &target_list); 923 list_add(&nt->list, &target_list);
@@ -799,6 +933,8 @@ static int __init init_netconsole(void)
799 if (err) 933 if (err)
800 goto undonotifier; 934 goto undonotifier;
801 935
936 if (netconsole_ext.flags & CON_ENABLED)
937 register_console(&netconsole_ext);
802 register_console(&netconsole); 938 register_console(&netconsole);
803 pr_info("network logging started\n"); 939 pr_info("network logging started\n");
804 940
@@ -827,6 +963,7 @@ static void __exit cleanup_netconsole(void)
827{ 963{
828 struct netconsole_target *nt, *tmp; 964 struct netconsole_target *nt, *tmp;
829 965
966 unregister_console(&netconsole_ext);
830 unregister_console(&netconsole); 967 unregister_console(&netconsole);
831 dynamic_netconsole_exit(); 968 dynamic_netconsole_exit();
832 unregister_netdevice_notifier(&netconsole_netdev_notifier); 969 unregister_netdevice_notifier(&netconsole_netdev_notifier);