diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-12 20:01:36 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-12 20:01:36 +0300 |
commit | 98d0052d0d9dcd5323833482712b5799ed0bbb0b (patch) | |
tree | e055e679d2aebf8b11d82b010775d96794295106 /drivers/tty | |
parent | 73fa58dca80293320f5cfeb06f5b2daeb8d97bd5 (diff) | |
parent | 6b2b0d839acaa84f05a77184370f793752e786e9 (diff) | |
download | linux-98d0052d0d9dcd5323833482712b5799ed0bbb0b.tar.xz |
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/tty')
-rw-r--r-- | drivers/tty/hvc/hvc_console.c | 4 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_core.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/earlycon.c | 4 | ||||
-rw-r--r-- | drivers/tty/serial/kgdboc.c | 46 | ||||
-rw-r--r-- | drivers/tty/serial/pic32_uart.c | 4 | ||||
-rw-r--r-- | drivers/tty/serial/samsung_tty.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/serial_core.c | 14 | ||||
-rw-r--r-- | drivers/tty/serial/sh-sci.c | 20 | ||||
-rw-r--r-- | drivers/tty/serial/xilinx_uartps.c | 2 | ||||
-rw-r--r-- | drivers/tty/tty_io.c | 18 |
10 files changed, 90 insertions, 26 deletions
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); |