Skip to content

Commit

Permalink
Merge tag 'printk-for-6.2' of git://git.kernel.org/pub/scm/linux/kern…
Browse files Browse the repository at this point in the history
…el/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()
  ...
  • Loading branch information
torvalds committed Dec 12, 2022
2 parents 73fa58d + 6b2b0d8 commit 98d0052
Show file tree
Hide file tree
Showing 32 changed files with 725 additions and 237 deletions.
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ ForEachMacros:
- 'for_each_component_dais'
- 'for_each_component_dais_safe'
- 'for_each_console'
- 'for_each_console_srcu'
- 'for_each_cpu'
- 'for_each_cpu_and'
- 'for_each_cpu_not'
Expand Down
9 changes: 7 additions & 2 deletions arch/m68k/emu/nfcon.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ static void nfcon_write(struct console *con, const char *str,
static struct tty_driver *nfcon_device(struct console *con, int *index)
{
*index = 0;
return (con->flags & CON_ENABLED) ? nfcon_tty_driver : NULL;
return console_is_registered(con) ? nfcon_tty_driver : NULL;
}

static struct console nf_console = {
Expand Down Expand Up @@ -107,6 +107,11 @@ static int __init nf_debug_setup(char *arg)

stderr_id = nf_get_id("NF_STDERR");
if (stderr_id) {
/*
* The console will be enabled when debug=nfcon is specified
* as a kernel parameter. Since this is a non-standard way
* of enabling consoles, it must be explicitly enabled.
*/
nf_console.flags |= CON_ENABLED;
register_console(&nf_console);
}
Expand Down Expand Up @@ -151,7 +156,7 @@ static int __init nfcon_init(void)

nfcon_tty_driver = driver;

if (!(nf_console.flags & CON_ENABLED))
if (!console_is_registered(&nf_console))
register_console(&nf_console);

return 0;
Expand Down
24 changes: 15 additions & 9 deletions arch/um/kernel/kmsg_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,26 @@ static void kmsg_dumper_stdout(struct kmsg_dumper *dumper,
struct console *con;
unsigned long flags;
size_t len = 0;
int cookie;

/* only dump kmsg when no console is available */
if (!console_trylock())
return;
/*
* If no consoles are available to output crash information, dump
* the kmsg buffer to stdout.
*/

for_each_console(con) {
if(strcmp(con->name, "tty") == 0 &&
(con->flags & (CON_ENABLED | CON_CONSDEV)) != 0) {
cookie = console_srcu_read_lock();
for_each_console_srcu(con) {
/*
* The ttynull console and disabled consoles are ignored
* since they cannot output. All other consoles are
* expected to output the crash information.
*/
if (strcmp(con->name, "ttynull") != 0 &&
(console_srcu_read_flags(con) & CON_ENABLED)) {
break;
}
}

console_unlock();

console_srcu_read_unlock(cookie);
if (con)
return;

Expand Down
8 changes: 4 additions & 4 deletions drivers/firmware/efi/earlycon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
}
Expand Down
21 changes: 11 additions & 10 deletions drivers/net/netconsole.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
};

Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions drivers/tty/hvc/hvc_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion drivers/tty/serial/8250/8250_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions drivers/tty/serial/earlycon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
46 changes: 41 additions & 5 deletions drivers/tty/serial/kgdboc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -202,6 +220,10 @@ static int configure_kgdboc(void)
}
}

console_unlock();

console_list_unlock();

kgdb_tty_driver = p;
kgdb_tty_line = tty_line;

Expand Down Expand Up @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -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)) &&
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions drivers/tty/serial/pic32_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down
2 changes: 1 addition & 1 deletion drivers/tty/serial/samsung_tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
14 changes: 7 additions & 7 deletions drivers/tty/serial/serial_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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);

/*
Expand Down Expand Up @@ -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');
Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 98d0052

Please sign in to comment.