summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-12-12 09:01:36 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2022-12-12 09:01:36 -0800
commit98d0052d0d9dcd5323833482712b5799ed0bbb0b (patch)
treee055e679d2aebf8b11d82b010775d96794295106 /drivers
parent73fa58dca80293320f5cfeb06f5b2daeb8d97bd5 (diff)
parent6b2b0d839acaa84f05a77184370f793752e786e9 (diff)
Merge tag 'printk-for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux
Pull printk updates from Petr Mladek: - Add NMI-safe SRCU reader API. It uses atomic_inc() instead of this_cpu_inc() on strong load-store architectures. - Introduce new console_list_lock to synchronize a manipulation of the list of registered consoles and their flags. This is a first step in removing the big-kernel-lock-like behavior of console_lock(). This semaphore still serializes console->write() calbacks against: - each other. It primary prevents potential races between early and proper console drivers using the same device. - suspend()/resume() callbacks and init() operations in some drivers. - various other operations in the tty/vt and framebufer susbsystems. It is likely that console_lock() serializes even operations that are not directly conflicting with the console->write() callbacks here. This is the most complicated big-kernel-lock aspect of the console_lock() that will be hard to untangle. - Introduce new console_srcu lock that is used to safely iterate and access the registered console drivers under SRCU read lock. This is a prerequisite for introducing atomic console drivers and console kthreads. It will reduce the complexity of serialization against normal consoles and console_lock(). Also it should remove the risk of deadlock during critical situations, like Oops or panic, when only atomic consoles are registered. - Check whether the console is registered instead of enabled on many locations. It was a historical leftover. - Cleanly force a preferred console in xenfb code instead of a dirty hack. - A lot of code and comment clean ups and improvements. * tag 'printk-for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux: (47 commits) printk: htmldocs: add missing description tty: serial: sh-sci: use setup() callback for early console printk: relieve console_lock of list synchronization duties tty: serial: kgdboc: use console_list_lock to trap exit tty: serial: kgdboc: synchronize tty_find_polling_driver() and register_console() tty: serial: kgdboc: use console_list_lock for list traversal tty: serial: kgdboc: use srcu console list iterator proc: consoles: use console_list_lock for list iteration tty: tty_io: use console_list_lock for list synchronization printk, xen: fbfront: create/use safe function for forcing preferred netconsole: avoid CON_ENABLED misuse to track registration usb: early: xhci-dbc: use console_is_registered() tty: serial: xilinx_uartps: use console_is_registered() tty: serial: samsung_tty: use console_is_registered() tty: serial: pic32_uart: use console_is_registered() tty: serial: earlycon: use console_is_registered() tty: hvc: use console_is_registered() efi: earlycon: use console_is_registered() tty: nfcon: use console_is_registered() serial_core: replace uart_console_enabled() with uart_console_registered() ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/firmware/efi/earlycon.c8
-rw-r--r--drivers/net/netconsole.c21
-rw-r--r--drivers/tty/hvc/hvc_console.c4
-rw-r--r--drivers/tty/serial/8250/8250_core.c2
-rw-r--r--drivers/tty/serial/earlycon.c4
-rw-r--r--drivers/tty/serial/kgdboc.c46
-rw-r--r--drivers/tty/serial/pic32_uart.c4
-rw-r--r--drivers/tty/serial/samsung_tty.c2
-rw-r--r--drivers/tty/serial/serial_core.c14
-rw-r--r--drivers/tty/serial/sh-sci.c20
-rw-r--r--drivers/tty/serial/xilinx_uartps.c2
-rw-r--r--drivers/tty/tty_io.c18
-rw-r--r--drivers/usb/early/xhci-dbc.c2
-rw-r--r--drivers/video/fbdev/xen-fbfront.c12
14 files changed, 110 insertions, 49 deletions
diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c
index a52236e11e5f..4d6c5327471a 100644
--- a/drivers/firmware/efi/earlycon.c
+++ b/drivers/firmware/efi/earlycon.c
@@ -29,8 +29,8 @@ static void *efi_fb;
*/
static int __init efi_earlycon_remap_fb(void)
{
- /* bail if there is no bootconsole or it has been disabled already */
- if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
+ /* bail if there is no bootconsole or it was unregistered already */
+ if (!earlycon_console || !console_is_registered(earlycon_console))
return 0;
efi_fb = memremap(fb_base, screen_info.lfb_size,
@@ -42,8 +42,8 @@ early_initcall(efi_earlycon_remap_fb);
static int __init efi_earlycon_unmap_fb(void)
{
- /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
- if (efi_fb && !(earlycon_console->flags & CON_ENABLED))
+ /* unmap the bootconsole fb unless keep_bootcon left it registered */
+ if (efi_fb && !console_is_registered(earlycon_console))
memunmap(efi_fb);
return 0;
}
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index bdff9ac5056d..4f4f79532c6c 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -332,10 +332,8 @@ static ssize_t enabled_store(struct config_item *item,
}
if (enabled) { /* true */
- if (nt->extended && !(netconsole_ext.flags & CON_ENABLED)) {
- netconsole_ext.flags |= CON_ENABLED;
+ if (nt->extended && !console_is_registered(&netconsole_ext))
register_console(&netconsole_ext);
- }
/*
* Skip netpoll_parse_options() -- all the attributes are
@@ -869,7 +867,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
static struct console netconsole_ext = {
.name = "netcon_ext",
- .flags = CON_EXTENDED, /* starts disabled, registered on first use */
+ .flags = CON_ENABLED | CON_EXTENDED,
.write = write_ext_msg,
};
@@ -883,6 +881,7 @@ static int __init init_netconsole(void)
{
int err;
struct netconsole_target *nt, *tmp;
+ bool extended = false;
unsigned long flags;
char *target_config;
char *input = config;
@@ -895,11 +894,12 @@ static int __init init_netconsole(void)
goto fail;
}
/* Dump existing printks when we register */
- if (nt->extended)
- netconsole_ext.flags |= CON_PRINTBUFFER |
- CON_ENABLED;
- else
+ if (nt->extended) {
+ extended = true;
+ netconsole_ext.flags |= CON_PRINTBUFFER;
+ } else {
netconsole.flags |= CON_PRINTBUFFER;
+ }
spin_lock_irqsave(&target_list_lock, flags);
list_add(&nt->list, &target_list);
@@ -915,7 +915,7 @@ static int __init init_netconsole(void)
if (err)
goto undonotifier;
- if (netconsole_ext.flags & CON_ENABLED)
+ if (extended)
register_console(&netconsole_ext);
register_console(&netconsole);
pr_info("network logging started\n");
@@ -945,7 +945,8 @@ static void __exit cleanup_netconsole(void)
{
struct netconsole_target *nt, *tmp;
- unregister_console(&netconsole_ext);
+ if (console_is_registered(&netconsole_ext))
+ unregister_console(&netconsole_ext);
unregister_console(&netconsole);
dynamic_netconsole_exit();
unregister_netdevice_notifier(&netconsole_netdev_notifier);
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 4802cfaa107f..a683e21df19c 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -264,8 +264,8 @@ static void hvc_port_destruct(struct tty_port *port)
static void hvc_check_console(int index)
{
- /* Already enabled, bail out */
- if (hvc_console.flags & CON_ENABLED)
+ /* Already registered, bail out */
+ if (console_is_registered(&hvc_console))
return;
/* If this index is what the user requested, then register
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 94fbf0add2ce..74568292186f 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -565,7 +565,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
up->port.dev = dev;
- if (uart_console_enabled(&up->port))
+ if (uart_console_registered(&up->port))
pm_runtime_get_sync(up->port.dev);
serial8250_apply_quirks(up);
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index a5f380584cda..4f6e9bf57169 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -181,7 +181,7 @@ int __init setup_earlycon(char *buf)
if (!buf || !buf[0])
return -EINVAL;
- if (early_con.flags & CON_ENABLED)
+ if (console_is_registered(&early_con))
return -EALREADY;
again:
@@ -253,7 +253,7 @@ int __init of_setup_earlycon(const struct earlycon_id *match,
bool big_endian;
u64 addr;
- if (early_con.flags & CON_ENABLED)
+ if (console_is_registered(&early_con))
return -EALREADY;
spin_lock_init(&port->lock);
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 7aa37be3216a..a3ed9b34e2ab 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -189,9 +189,27 @@ static int configure_kgdboc(void)
if (kgdboc_register_kbd(&cptr))
goto do_register;
+ /*
+ * tty_find_polling_driver() can call uart_set_options()
+ * (via poll_init) to configure the uart. Take the console_list_lock
+ * in order to synchronize against register_console(), which can also
+ * configure the uart via uart_set_options(). This also allows safe
+ * traversal of the console list.
+ */
+ console_list_lock();
+
p = tty_find_polling_driver(cptr, &tty_line);
- if (!p)
+ if (!p) {
+ console_list_unlock();
goto noconfig;
+ }
+
+ /*
+ * Take console_lock to serialize device() callback with
+ * other console operations. For example, fg_console is
+ * modified under console_lock when switching vt.
+ */
+ console_lock();
for_each_console(cons) {
int idx;
@@ -202,6 +220,10 @@ static int configure_kgdboc(void)
}
}
+ console_unlock();
+
+ console_list_unlock();
+
kgdb_tty_driver = p;
kgdb_tty_line = tty_line;
@@ -449,6 +471,7 @@ static void kgdboc_earlycon_pre_exp_handler(void)
{
struct console *con;
static bool already_warned;
+ int cookie;
if (already_warned)
return;
@@ -461,9 +484,14 @@ static void kgdboc_earlycon_pre_exp_handler(void)
* serial drivers might be OK with this, print a warning once per
* boot if we detect this case.
*/
- for_each_console(con)
+ cookie = console_srcu_read_lock();
+ for_each_console_srcu(con) {
if (con == kgdboc_earlycon_io_ops.cons)
- return;
+ break;
+ }
+ console_srcu_read_unlock(cookie);
+ if (con)
+ return;
already_warned = true;
pr_warn("kgdboc_earlycon is still using bootconsole\n");
@@ -528,7 +556,15 @@ static int __init kgdboc_earlycon_init(char *opt)
* Look for a matching console, or if the name was left blank just
* pick the first one we find.
*/
- console_lock();
+
+ /*
+ * Hold the console_list_lock to guarantee that no consoles are
+ * unregistered until the kgdboc_earlycon setup is complete.
+ * Trapping the exit() callback relies on exit() not being
+ * called until the trap is setup. This also allows safe
+ * traversal of the console list and race-free reading of @flags.
+ */
+ console_list_lock();
for_each_console(con) {
if (con->write && con->read &&
(con->flags & (CON_BOOT | CON_ENABLED)) &&
@@ -570,7 +606,7 @@ static int __init kgdboc_earlycon_init(char *opt)
}
unlock:
- console_unlock();
+ console_list_unlock();
/* Non-zero means malformed option so we always return zero */
return 0;
diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
index 2beada66c824..c38754d593ca 100644
--- a/drivers/tty/serial/pic32_uart.c
+++ b/drivers/tty/serial/pic32_uart.c
@@ -843,7 +843,7 @@ console_initcall(pic32_console_init);
*/
static int __init pic32_late_console_init(void)
{
- if (!(pic32_console.flags & CON_ENABLED))
+ if (!console_is_registered(&pic32_console))
register_console(&pic32_console);
return 0;
@@ -919,7 +919,7 @@ static int pic32_uart_probe(struct platform_device *pdev)
}
#ifdef CONFIG_SERIAL_PIC32_CONSOLE
- if (uart_console_enabled(port)) {
+ if (uart_console_registered(port)) {
/* The peripheral clock has been enabled by console_setup,
* so disable it till the port is used.
*/
diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
index 77d1363029f5..9c252c9ca95a 100644
--- a/drivers/tty/serial/samsung_tty.c
+++ b/drivers/tty/serial/samsung_tty.c
@@ -1732,7 +1732,7 @@ static void __init s3c24xx_serial_register_console(void)
static void s3c24xx_serial_unregister_console(void)
{
- if (s3c24xx_serial_console.flags & CON_ENABLED)
+ if (console_is_registered(&s3c24xx_serial_console))
unregister_console(&s3c24xx_serial_console);
}
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 179ee199df34..b9fbbee598b8 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2223,11 +2223,11 @@ uart_set_options(struct uart_port *port, struct console *co,
/*
* Ensure that the serial-console lock is initialised early.
*
- * Note that the console-enabled check is needed because of kgdboc,
- * which can end up calling uart_set_options() for an already enabled
+ * Note that the console-registered check is needed because
+ * kgdboc can call uart_set_options() for an already registered
* console via tty_find_polling_driver() and uart_poll_init().
*/
- if (!uart_console_enabled(port) && !port->console_reinit)
+ if (!uart_console_registered_locked(port) && !port->console_reinit)
uart_port_spin_lock_init(port);
memset(&termios, 0, sizeof(struct ktermios));
@@ -2573,7 +2573,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
* successfully registered yet, try to re-register it.
* It may be that the port was not available.
*/
- if (port->cons && !(port->cons->flags & CON_ENABLED))
+ if (port->cons && !console_is_registered(port->cons))
register_console(port->cons);
/*
@@ -2956,7 +2956,7 @@ static ssize_t console_show(struct device *dev,
mutex_lock(&port->mutex);
uport = uart_port_check(state);
if (uport)
- console = uart_console_enabled(uport);
+ console = uart_console_registered(uport);
mutex_unlock(&port->mutex);
return sprintf(buf, "%c\n", console ? 'Y' : 'N');
@@ -2978,7 +2978,7 @@ static ssize_t console_store(struct device *dev,
mutex_lock(&port->mutex);
uport = uart_port_check(state);
if (uport) {
- oldconsole = uart_console_enabled(uport);
+ oldconsole = uart_console_registered(uport);
if (oldconsole && !newconsole) {
ret = unregister_console(uport->cons);
} else if (!oldconsole && newconsole) {
@@ -3086,7 +3086,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
* If this port is in use as a console then the spinlock is already
* initialised.
*/
- if (!uart_console_enabled(uport))
+ if (!uart_console_registered(uport))
uart_port_spin_lock_init(uport);
if (uport->cons && uport->dev)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 62f773286d44..76452fe2af86 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -3054,15 +3054,29 @@ static struct console serial_console = {
};
#ifdef CONFIG_SUPERH
+static char early_serial_buf[32];
+
+static int early_serial_console_setup(struct console *co, char *options)
+{
+ /*
+ * This early console is always registered using the earlyprintk=
+ * parameter, which does not call add_preferred_console(). Thus
+ * @options is always NULL and the options for this early console
+ * are passed using a custom buffer.
+ */
+ WARN_ON(options);
+
+ return serial_console_setup(co, early_serial_buf);
+}
+
static struct console early_serial_console = {
.name = "early_ttySC",
.write = serial_console_write,
+ .setup = early_serial_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
-static char early_serial_buf[32];
-
static int sci_probe_earlyprintk(struct platform_device *pdev)
{
const struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev);
@@ -3074,8 +3088,6 @@ static int sci_probe_earlyprintk(struct platform_device *pdev)
sci_init_single(pdev, &sci_ports[pdev->id], pdev->id, cfg, true);
- serial_console_setup(&early_serial_console, early_serial_buf);
-
if (!strstr(early_serial_buf, "keep"))
early_serial_console.flags |= CON_BOOT;
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 2eff7cff57c4..0cbd1892c53b 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1631,7 +1631,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
/* This is not port which is used for console that's why clean it up */
if (console_port == port &&
- !(cdns_uart_uart_driver.cons->flags & CON_ENABLED)) {
+ !console_is_registered(cdns_uart_uart_driver.cons)) {
console_port = NULL;
cdns_uart_console.index = -1;
}
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index de06c3c2ff70..cafdff575716 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -3526,7 +3526,14 @@ static ssize_t show_cons_active(struct device *dev,
struct console *c;
ssize_t count = 0;
- console_lock();
+ /*
+ * Hold the console_list_lock to guarantee that no consoles are
+ * unregistered until all console processing is complete.
+ * This also allows safe traversal of the console list and
+ * race-free reading of @flags.
+ */
+ console_list_lock();
+
for_each_console(c) {
if (!c->device)
continue;
@@ -3538,6 +3545,13 @@ static ssize_t show_cons_active(struct device *dev,
if (i >= ARRAY_SIZE(cs))
break;
}
+
+ /*
+ * Take console_lock to serialize device() callback with
+ * other console operations. For example, fg_console is
+ * modified under console_lock when switching vt.
+ */
+ console_lock();
while (i--) {
int index = cs[i]->index;
struct tty_driver *drv = cs[i]->device(cs[i], &index);
@@ -3553,6 +3567,8 @@ static ssize_t show_cons_active(struct device *dev,
}
console_unlock();
+ console_list_unlock();
+
return count;
}
static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL);
diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c
index bfb7e2b85299..797047154820 100644
--- a/drivers/usb/early/xhci-dbc.c
+++ b/drivers/usb/early/xhci-dbc.c
@@ -927,7 +927,7 @@ void __init early_xdbc_register_console(void)
static void xdbc_unregister_console(void)
{
- if (early_xdbc_console.flags & CON_ENABLED)
+ if (console_is_registered(&early_xdbc_console))
unregister_console(&early_xdbc_console);
}
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index 4d2694d904aa..8752d389e382 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -504,18 +504,14 @@ static void xenfb_make_preferred_console(void)
if (console_set_on_cmdline)
return;
- console_lock();
+ console_list_lock();
for_each_console(c) {
if (!strcmp(c->name, "tty") && c->index == 0)
break;
}
- console_unlock();
- if (c) {
- unregister_console(c);
- c->flags |= CON_CONSDEV;
- c->flags &= ~CON_PRINTBUFFER; /* don't print again */
- register_console(c);
- }
+ if (c)
+ console_force_preferred_locked(c);
+ console_list_unlock();
}
static int xenfb_resume(struct xenbus_device *dev)