aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/netconsole.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r--drivers/net/netconsole.c169
1 files changed, 151 insertions, 18 deletions
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 15731d1db918..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.
@@ -104,14 +110,15 @@ struct netconsole_target {
104#ifdef CONFIG_NETCONSOLE_DYNAMIC 110#ifdef CONFIG_NETCONSOLE_DYNAMIC
105 struct config_item item; 111 struct config_item item;
106#endif 112#endif
107 int enabled; 113 bool enabled;
108 struct mutex mutex; 114 bool extended;
109 struct netpoll np; 115 struct netpoll np;
110}; 116};
111 117
112#ifdef CONFIG_NETCONSOLE_DYNAMIC 118#ifdef CONFIG_NETCONSOLE_DYNAMIC
113 119
114static struct configfs_subsystem netconsole_subsys; 120static struct configfs_subsystem netconsole_subsys;
121static DEFINE_MUTEX(dynamic_netconsole_mutex);
115 122
116static int __init dynamic_netconsole_init(void) 123static int __init dynamic_netconsole_init(void)
117{ 124{
@@ -185,9 +192,13 @@ static struct netconsole_target *alloc_param_target(char *target_config)
185 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ); 192 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
186 nt->np.local_port = 6665; 193 nt->np.local_port = 6665;
187 nt->np.remote_port = 6666; 194 nt->np.remote_port = 6666;
188 mutex_init(&nt->mutex);
189 eth_broadcast_addr(nt->np.remote_mac); 195 eth_broadcast_addr(nt->np.remote_mac);
190 196
197 if (*target_config == '+') {
198 nt->extended = true;
199 target_config++;
200 }
201
191 /* Parse parameters and setup netpoll */ 202 /* Parse parameters and setup netpoll */
192 err = netpoll_parse_options(&nt->np, target_config); 203 err = netpoll_parse_options(&nt->np, target_config);
193 if (err) 204 if (err)
@@ -197,7 +208,7 @@ static struct netconsole_target *alloc_param_target(char *target_config)
197 if (err) 208 if (err)
198 goto fail; 209 goto fail;
199 210
200 nt->enabled = 1; 211 nt->enabled = true;
201 212
202 return nt; 213 return nt;
203 214
@@ -258,6 +269,11 @@ static ssize_t show_enabled(struct netconsole_target *nt, char *buf)
258 return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled); 269 return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled);
259} 270}
260 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
261static ssize_t show_dev_name(struct netconsole_target *nt, char *buf) 277static ssize_t show_dev_name(struct netconsole_target *nt, char *buf)
262{ 278{
263 return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name); 279 return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name);
@@ -322,13 +338,18 @@ static ssize_t store_enabled(struct netconsole_target *nt,
322 return err; 338 return err;
323 if (enabled < 0 || enabled > 1) 339 if (enabled < 0 || enabled > 1)
324 return -EINVAL; 340 return -EINVAL;
325 if (enabled == nt->enabled) { 341 if ((bool)enabled == nt->enabled) {
326 pr_info("network logging has already %s\n", 342 pr_info("network logging has already %s\n",
327 nt->enabled ? "started" : "stopped"); 343 nt->enabled ? "started" : "stopped");
328 return -EINVAL; 344 return -EINVAL;
329 } 345 }
330 346
331 if (enabled) { /* 1 */ 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
332 /* 353 /*
333 * Skip netpoll_parse_options() -- all the attributes are 354 * Skip netpoll_parse_options() -- all the attributes are
334 * already configured via configfs. Just print them out. 355 * already configured via configfs. Just print them out.
@@ -340,13 +361,13 @@ static ssize_t store_enabled(struct netconsole_target *nt,
340 return err; 361 return err;
341 362
342 pr_info("netconsole: network logging started\n"); 363 pr_info("netconsole: network logging started\n");
343 } else { /* 0 */ 364 } else { /* false */
344 /* We need to disable the netconsole before cleaning it up 365 /* We need to disable the netconsole before cleaning it up
345 * otherwise we might end up in write_msg() with 366 * otherwise we might end up in write_msg() with
346 * nt->np.dev == NULL and nt->enabled == 1 367 * nt->np.dev == NULL and nt->enabled == true
347 */ 368 */
348 spin_lock_irqsave(&target_list_lock, flags); 369 spin_lock_irqsave(&target_list_lock, flags);
349 nt->enabled = 0; 370 nt->enabled = false;
350 spin_unlock_irqrestore(&target_list_lock, flags); 371 spin_unlock_irqrestore(&target_list_lock, flags);
351 netpoll_cleanup(&nt->np); 372 netpoll_cleanup(&nt->np);
352 } 373 }
@@ -356,6 +377,30 @@ static ssize_t store_enabled(struct netconsole_target *nt,
356 return strnlen(buf, count); 377 return strnlen(buf, count);
357} 378}
358 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
359static ssize_t store_dev_name(struct netconsole_target *nt, 404static ssize_t store_dev_name(struct netconsole_target *nt,
360 const char *buf, 405 const char *buf,
361 size_t count) 406 size_t count)
@@ -508,6 +553,7 @@ static struct netconsole_target_attr netconsole_target_##_name = \
508 __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name) 553 __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name)
509 554
510NETCONSOLE_TARGET_ATTR_RW(enabled); 555NETCONSOLE_TARGET_ATTR_RW(enabled);
556NETCONSOLE_TARGET_ATTR_RW(extended);
511NETCONSOLE_TARGET_ATTR_RW(dev_name); 557NETCONSOLE_TARGET_ATTR_RW(dev_name);
512NETCONSOLE_TARGET_ATTR_RW(local_port); 558NETCONSOLE_TARGET_ATTR_RW(local_port);
513NETCONSOLE_TARGET_ATTR_RW(remote_port); 559NETCONSOLE_TARGET_ATTR_RW(remote_port);
@@ -518,6 +564,7 @@ NETCONSOLE_TARGET_ATTR_RW(remote_mac);
518 564
519static struct configfs_attribute *netconsole_target_attrs[] = { 565static struct configfs_attribute *netconsole_target_attrs[] = {
520 &netconsole_target_enabled.attr, 566 &netconsole_target_enabled.attr,
567 &netconsole_target_extended.attr,
521 &netconsole_target_dev_name.attr, 568 &netconsole_target_dev_name.attr,
522 &netconsole_target_local_port.attr, 569 &netconsole_target_local_port.attr,
523 &netconsole_target_remote_port.attr, 570 &netconsole_target_remote_port.attr,
@@ -562,10 +609,10 @@ static ssize_t netconsole_target_attr_store(struct config_item *item,
562 struct netconsole_target_attr *na = 609 struct netconsole_target_attr *na =
563 container_of(attr, struct netconsole_target_attr, attr); 610 container_of(attr, struct netconsole_target_attr, attr);
564 611
565 mutex_lock(&nt->mutex); 612 mutex_lock(&dynamic_netconsole_mutex);
566 if (na->store) 613 if (na->store)
567 ret = na->store(nt, buf, count); 614 ret = na->store(nt, buf, count);
568 mutex_unlock(&nt->mutex); 615 mutex_unlock(&dynamic_netconsole_mutex);
569 616
570 return ret; 617 return ret;
571} 618}
@@ -594,7 +641,7 @@ static struct config_item *make_netconsole_target(struct config_group *group,
594 641
595 /* 642 /*
596 * Allocate and initialize with defaults. 643 * Allocate and initialize with defaults.
597 * Target is disabled at creation (enabled == 0). 644 * Target is disabled at creation (!enabled).
598 */ 645 */
599 nt = kzalloc(sizeof(*nt), GFP_KERNEL); 646 nt = kzalloc(sizeof(*nt), GFP_KERNEL);
600 if (!nt) 647 if (!nt)
@@ -604,7 +651,6 @@ static struct config_item *make_netconsole_target(struct config_group *group,
604 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ); 651 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
605 nt->np.local_port = 6665; 652 nt->np.local_port = 6665;
606 nt->np.remote_port = 6666; 653 nt->np.remote_port = 6666;
607 mutex_init(&nt->mutex);
608 eth_broadcast_addr(nt->np.remote_mac); 654 eth_broadcast_addr(nt->np.remote_mac);
609 655
610 /* Initialize the config_item member */ 656 /* Initialize the config_item member */
@@ -695,7 +741,7 @@ restart:
695 spin_lock_irqsave(&target_list_lock, flags); 741 spin_lock_irqsave(&target_list_lock, flags);
696 dev_put(nt->np.dev); 742 dev_put(nt->np.dev);
697 nt->np.dev = NULL; 743 nt->np.dev = NULL;
698 nt->enabled = 0; 744 nt->enabled = false;
699 stopped = true; 745 stopped = true;
700 netconsole_target_put(nt); 746 netconsole_target_put(nt);
701 goto restart; 747 goto restart;
@@ -729,6 +775,82 @@ static struct notifier_block netconsole_netdev_notifier = {
729 .notifier_call = netconsole_netdev_event, 775 .notifier_call = netconsole_netdev_event,
730}; 776};
731 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
732static 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)
733{ 855{
734 int frag, left; 856 int frag, left;
@@ -744,8 +866,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
744 866
745 spin_lock_irqsave(&target_list_lock, flags); 867 spin_lock_irqsave(&target_list_lock, flags);
746 list_for_each_entry(nt, &target_list, list) { 868 list_for_each_entry(nt, &target_list, list) {
747 netconsole_target_get(nt); 869 if (!nt->extended && nt->enabled && netif_running(nt->np.dev)) {
748 if (nt->enabled && netif_running(nt->np.dev)) {
749 /* 870 /*
750 * We nest this inside the for-each-target loop above 871 * We nest this inside the for-each-target loop above
751 * 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
@@ -760,11 +881,16 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
760 left -= frag; 881 left -= frag;
761 } 882 }
762 } 883 }
763 netconsole_target_put(nt);
764 } 884 }
765 spin_unlock_irqrestore(&target_list_lock, flags); 885 spin_unlock_irqrestore(&target_list_lock, flags);
766} 886}
767 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
768static struct console netconsole = { 894static struct console netconsole = {
769 .name = "netcon", 895 .name = "netcon",
770 .flags = CON_ENABLED, 896 .flags = CON_ENABLED,
@@ -787,7 +913,11 @@ static int __init init_netconsole(void)
787 goto fail; 913 goto fail;
788 } 914 }
789 /* Dump existing printks when we register */ 915 /* Dump existing printks when we register */
790 netconsole.flags |= CON_PRINTBUFFER; 916 if (nt->extended)
917 netconsole_ext.flags |= CON_PRINTBUFFER |
918 CON_ENABLED;
919 else
920 netconsole.flags |= CON_PRINTBUFFER;
791 921
792 spin_lock_irqsave(&target_list_lock, flags); 922 spin_lock_irqsave(&target_list_lock, flags);
793 list_add(&nt->list, &target_list); 923 list_add(&nt->list, &target_list);
@@ -803,6 +933,8 @@ static int __init init_netconsole(void)
803 if (err) 933 if (err)
804 goto undonotifier; 934 goto undonotifier;
805 935
936 if (netconsole_ext.flags & CON_ENABLED)
937 register_console(&netconsole_ext);
806 register_console(&netconsole); 938 register_console(&netconsole);
807 pr_info("network logging started\n"); 939 pr_info("network logging started\n");
808 940
@@ -831,6 +963,7 @@ static void __exit cleanup_netconsole(void)
831{ 963{
832 struct netconsole_target *nt, *tmp; 964 struct netconsole_target *nt, *tmp;
833 965
966 unregister_console(&netconsole_ext);
834 unregister_console(&netconsole); 967 unregister_console(&netconsole);
835 dynamic_netconsole_exit(); 968 dynamic_netconsole_exit();
836 unregister_netdevice_notifier(&netconsole_netdev_notifier); 969 unregister_netdevice_notifier(&netconsole_netdev_notifier);