diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/net/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/net/netconsole.c | 153 |
2 files changed, 95 insertions, 59 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9c7953f8e637..7e9becad91df 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -341,6 +341,7 @@ config NETCONSOLE_DYNAMIC bool "Dynamic reconfiguration of logging targets" depends on NETCONSOLE && SYSFS && CONFIGFS_FS && \ !(NETCONSOLE=y && CONFIGFS_FS=m) + select PRINTK_EXECUTION_CTX help This option enables the ability to dynamically reconfigure target parameters (interface, IP addresses, port numbers, MAC addresses) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 82c232f9ede2..0f44ce5ccc0a 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -1490,18 +1490,20 @@ static void populate_configfs_item(struct netconsole_target *nt, init_target_config_group(nt, target_name); } -static int sysdata_append_cpu_nr(struct netconsole_target *nt, int offset) +static int sysdata_append_cpu_nr(struct netconsole_target *nt, int offset, + struct nbcon_write_context *wctxt) { return scnprintf(&nt->sysdata[offset], MAX_EXTRADATA_ENTRY_LEN, " cpu=%u\n", - raw_smp_processor_id()); + wctxt->cpu); } -static int sysdata_append_taskname(struct netconsole_target *nt, int offset) +static int sysdata_append_taskname(struct netconsole_target *nt, int offset, + struct nbcon_write_context *wctxt) { return scnprintf(&nt->sysdata[offset], MAX_EXTRADATA_ENTRY_LEN, " taskname=%s\n", - current->comm); + wctxt->comm); } static int sysdata_append_release(struct netconsole_target *nt, int offset) @@ -1522,8 +1524,10 @@ static int sysdata_append_msgid(struct netconsole_target *nt, int offset) /* * prepare_sysdata - append sysdata in runtime * @nt: target to send message to + * @wctxt: nbcon write context containing message metadata */ -static int prepare_sysdata(struct netconsole_target *nt) +static int prepare_sysdata(struct netconsole_target *nt, + struct nbcon_write_context *wctxt) { int sysdata_len = 0; @@ -1531,9 +1535,9 @@ static int prepare_sysdata(struct netconsole_target *nt) goto out; if (nt->sysdata_fields & SYSDATA_CPU_NR) - sysdata_len += sysdata_append_cpu_nr(nt, sysdata_len); + sysdata_len += sysdata_append_cpu_nr(nt, sysdata_len, wctxt); if (nt->sysdata_fields & SYSDATA_TASKNAME) - sysdata_len += sysdata_append_taskname(nt, sysdata_len); + sysdata_len += sysdata_append_taskname(nt, sysdata_len, wctxt); if (nt->sysdata_fields & SYSDATA_RELEASE) sysdata_len += sysdata_append_release(nt, sysdata_len); if (nt->sysdata_fields & SYSDATA_MSGID) @@ -1831,83 +1835,108 @@ static void send_msg_fragmented(struct netconsole_target *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 + * @wctxt: nbcon write context containing message and metadata * - * Transfer extended log @msg to @nt. If @msg is longer than + * Transfer extended log message to @nt. If message 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 void send_ext_msg_udp(struct netconsole_target *nt, + struct nbcon_write_context *wctxt) { int userdata_len = 0; int release_len = 0; int sysdata_len = 0; + int len; #ifdef CONFIG_NETCONSOLE_DYNAMIC - sysdata_len = prepare_sysdata(nt); + sysdata_len = prepare_sysdata(nt, wctxt); userdata_len = nt->userdata_length; #endif if (nt->release) release_len = strlen(init_utsname()->release) + 1; - if (msg_len + release_len + sysdata_len + userdata_len <= MAX_PRINT_CHUNK) - return send_msg_no_fragmentation(nt, msg, msg_len, release_len); + len = wctxt->len + release_len + sysdata_len + userdata_len; + if (len <= MAX_PRINT_CHUNK) + return send_msg_no_fragmentation(nt, wctxt->outbuf, + wctxt->len, release_len); - return send_msg_fragmented(nt, msg, msg_len, release_len, + return send_msg_fragmented(nt, wctxt->outbuf, wctxt->len, release_len, sysdata_len); } -static void write_ext_msg(struct console *con, const char *msg, - unsigned int len) +static void send_msg_udp(struct netconsole_target *nt, const char *msg, + unsigned int len) { - struct netconsole_target *nt; - unsigned long flags; - - if ((oops_only && !oops_in_progress) || list_empty(&target_list)) - return; + const char *tmp = msg; + int frag, left = len; - spin_lock_irqsave(&target_list_lock, flags); - list_for_each_entry(nt, &target_list, list) - if (nt->extended && nt->state == STATE_ENABLED && - netif_running(nt->np.dev)) - send_ext_msg_udp(nt, msg, len); - spin_unlock_irqrestore(&target_list_lock, flags); + while (left > 0) { + frag = min(left, MAX_PRINT_CHUNK); + send_udp(nt, tmp, frag); + tmp += frag; + left -= frag; + } } -static void write_msg(struct console *con, const char *msg, unsigned int len) +/** + * netconsole_write - Generic function to send a msg to all targets + * @wctxt: nbcon write context + * @extended: "true" for extended console mode + * + * Given an nbcon write context, send the message to the netconsole targets + */ +static void netconsole_write(struct nbcon_write_context *wctxt, bool extended) { - int frag, left; - unsigned long flags; struct netconsole_target *nt; - const char *tmp; if (oops_only && !oops_in_progress) return; - /* Avoid taking lock and disabling interrupts unnecessarily */ - if (list_empty(&target_list)) - return; - spin_lock_irqsave(&target_list_lock, flags); list_for_each_entry(nt, &target_list, list) { - if (!nt->extended && nt->state == STATE_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); - send_udp(nt, tmp, frag); - tmp += frag; - left -= frag; - } - } + if (nt->extended != extended || nt->state != STATE_ENABLED || + !netif_running(nt->np.dev)) + continue; + + /* If nbcon_enter_unsafe() fails, just return given netconsole + * lost the ownership, and iterating over the targets will not + * be able to re-acquire. + */ + if (!nbcon_enter_unsafe(wctxt)) + return; + + if (extended) + send_ext_msg_udp(nt, wctxt); + else + send_msg_udp(nt, wctxt->outbuf, wctxt->len); + + nbcon_exit_unsafe(wctxt); } +} + +static void netconsole_write_ext(struct console *con __always_unused, + struct nbcon_write_context *wctxt) +{ + netconsole_write(wctxt, true); +} + +static void netconsole_write_basic(struct console *con __always_unused, + struct nbcon_write_context *wctxt) +{ + netconsole_write(wctxt, false); +} + +static void netconsole_device_lock(struct console *con __always_unused, + unsigned long *flags) +__acquires(&target_list_lock) +{ + spin_lock_irqsave(&target_list_lock, *flags); +} + +static void netconsole_device_unlock(struct console *con __always_unused, + unsigned long flags) +__releases(&target_list_lock) +{ spin_unlock_irqrestore(&target_list_lock, flags); } @@ -2071,15 +2100,21 @@ static void free_param_target(struct netconsole_target *nt) } static struct console netconsole_ext = { - .name = "netcon_ext", - .flags = CON_ENABLED | CON_EXTENDED, - .write = write_ext_msg, + .name = "netcon_ext", + .flags = CON_ENABLED | CON_EXTENDED | CON_NBCON | CON_NBCON_ATOMIC_UNSAFE, + .write_thread = netconsole_write_ext, + .write_atomic = netconsole_write_ext, + .device_lock = netconsole_device_lock, + .device_unlock = netconsole_device_unlock, }; static struct console netconsole = { - .name = "netcon", - .flags = CON_ENABLED, - .write = write_msg, + .name = "netcon", + .flags = CON_ENABLED | CON_NBCON | CON_NBCON_ATOMIC_UNSAFE, + .write_thread = netconsole_write_basic, + .write_atomic = netconsole_write_basic, + .device_lock = netconsole_device_lock, + .device_unlock = netconsole_device_unlock, }; static int __init init_netconsole(void) |
