Commit 7eab73b1 authored by Breno Leitao's avatar Breno Leitao Committed by Jakub Kicinski
Browse files

netconsole: convert to NBCON console infrastructure

Convert netconsole from the legacy console API to the NBCON framework.
NBCON provides threaded printing which unblocks printk()s and flushes in
a thread, decoupling network TX from printk() when netconsole is
in use.

Since netconsole relies on the network stack which cannot safely operate
from all atomic contexts, mark both consoles with
CON_NBCON_ATOMIC_UNSAFE. (See discussion in [1])

CON_NBCON_ATOMIC_UNSAFE restricts write_atomic() usage to emergency
scenarios (panic) where regular messages are sent in threaded mode.

Implementation changes:
- Unify write_ext_msg() and write_msg() into netconsole_write()
- Add device_lock/device_unlock callbacks to manage target_list_lock
- Use nbcon_enter_unsafe()/nbcon_exit_unsafe() around network
  operations.
  - If nbcon_enter_unsafe() fails, just return given netconsole lost
    the ownership of the console.
- Set write_thread and write_atomic callbacks (both use same function)

Link: https://lore.kernel.org/all/b2qps3uywhmjaym4mht2wpxul4yqtuuayeoq4iv4k3zf5wdgh3@tocu6c7mj4lt/

 [1]
Reviewed-by: default avatarJohn Ogness <john.ogness@linutronix.de>
Reviewed-by: default avatarPetr Mladek <pmladek@suse.com>
Signed-off-by: default avatarBreno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20260206-nbcon-v7-3-62bda69b1b41@debian.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent eaf35bc6
Loading
Loading
Loading
Loading
+62 −39
Original line number Diff line number Diff line
@@ -1859,23 +1859,6 @@ static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
				   sysdata_len);
}

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)
		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);
}

static void send_msg_udp(struct netconsole_target *nt, const char *msg,
			 unsigned int len)
{
@@ -1890,30 +1873,64 @@ static void send_msg_udp(struct netconsole_target *nt, const char *msg,
	}
}

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)
{
	unsigned long flags;
	struct netconsole_target *nt;

	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.
		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.
		 */
			send_msg_udp(nt, msg, len);
		if (!nbcon_enter_unsafe(wctxt))
			return;

		if (extended)
			send_ext_msg_udp(nt, wctxt->outbuf, wctxt->len);
		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);
}

@@ -2078,14 +2095,20 @@ 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,
	.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,
	.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)