mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 16:53:49 +00:00
printk changes for 6.2
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEESH4wyp42V4tXvYsjUqAMR0iAlPIFAmORzikACgkQUqAMR0iA lPKF/g/7Bmcao3rJkZjEagsYY+s7rGhaFaSbML8FDdyE3UzeXLJOnNxBLrD0JIe9 XFW7+DMqr2uRxsab5C7APy0mrIWp/zCGyJ8CmBILnrPDNcAQ27OhFzxv6WlMUmEc xEjGHrk5dFV96s63gyHGLkKGOZMd/cfcpy/QDOyg0vfF8EZCiPywWMbQQ2Ij8E50 N6UL70ExkoLjT9tzb8NXQiaDqHxqNRvd15aIomDjRrce7eeaL4TaZIT7fKnEcULz 0Lmdo8RUknonCI7Y00RWdVXMqqPD2JsKz3+fh0vBnXEN+aItwyxis/YajtN+m6l7 jhPGt7hNhCKG17auK0/6XVJ3717QwjI3+xLXCvayA8jyewMK14PgzX70hCws0eXM +5M+IeXI4ze5qsq+ln9Dt8zfC+5HGmwXODUtaYTBWhB4nVWdL/CZ+nTv349zt+Uc VIi/QcPQ4vq6EfsxUZR2r6Y12+sSH40iLIROUfqSchtujbLo7qxSNF5x7x9+rtff nWuXo5OsjGE7TZDwn3kr0zSuJ+w/pkWMYQ7jch+A2WqUMYyGC86sL3At7ocL+Esq 34uvzwEgWnNySV8cLiMh34kBmgBwhAP34RhV0RS9iCv8kev2DV7pLQTs9V3QAjw9 EZnFDHATUdikgugaFKCeDV86R3wFgnRWWOdlRrRi6aAzFDqNcYk= =1PTZ -----END PGP SIGNATURE----- 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() ...
This commit is contained in:
commit
98d0052d0d
@ -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'
|
||||
|
@ -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 = {
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -33,7 +33,16 @@ static int show_console_dev(struct seq_file *m, void *v)
|
||||
if (con->device) {
|
||||
const struct tty_driver *driver;
|
||||
int index;
|
||||
|
||||
/*
|
||||
* 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();
|
||||
driver = con->device(con, &index);
|
||||
console_unlock();
|
||||
|
||||
if (driver) {
|
||||
dev = MKDEV(driver->major, driver->minor_start);
|
||||
dev += index;
|
||||
@ -63,7 +72,12 @@ static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
struct console *con;
|
||||
loff_t off = 0;
|
||||
|
||||
console_lock();
|
||||
/*
|
||||
* Hold the console_list_lock to guarantee safe traversal of the
|
||||
* console list. SRCU cannot be used because there is no
|
||||
* place to store the SRCU cookie.
|
||||
*/
|
||||
console_list_lock();
|
||||
for_each_console(con)
|
||||
if (off++ == *pos)
|
||||
break;
|
||||
@ -74,13 +88,14 @@ static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
struct console *con = v;
|
||||
|
||||
++*pos;
|
||||
return con->next;
|
||||
return hlist_entry_safe(con->node.next, struct console, node);
|
||||
}
|
||||
|
||||
static void c_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
console_unlock();
|
||||
console_list_unlock();
|
||||
}
|
||||
|
||||
static const struct seq_operations consoles_op = {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define _LINUX_CONSOLE_H_ 1
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct vc_data;
|
||||
@ -154,14 +155,132 @@ struct console {
|
||||
u64 seq;
|
||||
unsigned long dropped;
|
||||
void *data;
|
||||
struct console *next;
|
||||
struct hlist_node node;
|
||||
};
|
||||
|
||||
/*
|
||||
* for_each_console() allows you to iterate on each console
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
extern void lockdep_assert_console_list_lock_held(void);
|
||||
#else
|
||||
static inline void lockdep_assert_console_list_lock_held(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
extern bool console_srcu_read_lock_is_held(void);
|
||||
#else
|
||||
static inline bool console_srcu_read_lock_is_held(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern int console_srcu_read_lock(void);
|
||||
extern void console_srcu_read_unlock(int cookie);
|
||||
|
||||
extern void console_list_lock(void) __acquires(console_mutex);
|
||||
extern void console_list_unlock(void) __releases(console_mutex);
|
||||
|
||||
extern struct hlist_head console_list;
|
||||
|
||||
/**
|
||||
* console_srcu_read_flags - Locklessly read the console flags
|
||||
* @con: struct console pointer of console to read flags from
|
||||
*
|
||||
* This function provides the necessary READ_ONCE() and data_race()
|
||||
* notation for locklessly reading the console flags. The READ_ONCE()
|
||||
* in this function matches the WRITE_ONCE() when @flags are modified
|
||||
* for registered consoles with console_srcu_write_flags().
|
||||
*
|
||||
* Only use this function to read console flags when locklessly
|
||||
* iterating the console list via srcu.
|
||||
*
|
||||
* Context: Any context.
|
||||
*/
|
||||
#define for_each_console(con) \
|
||||
for (con = console_drivers; con != NULL; con = con->next)
|
||||
static inline short console_srcu_read_flags(const struct console *con)
|
||||
{
|
||||
WARN_ON_ONCE(!console_srcu_read_lock_is_held());
|
||||
|
||||
/*
|
||||
* Locklessly reading console->flags provides a consistent
|
||||
* read value because there is at most one CPU modifying
|
||||
* console->flags and that CPU is using only read-modify-write
|
||||
* operations to do so.
|
||||
*/
|
||||
return data_race(READ_ONCE(con->flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* console_srcu_write_flags - Write flags for a registered console
|
||||
* @con: struct console pointer of console to write flags to
|
||||
* @flags: new flags value to write
|
||||
*
|
||||
* Only use this function to write flags for registered consoles. It
|
||||
* requires holding the console_list_lock.
|
||||
*
|
||||
* Context: Any context.
|
||||
*/
|
||||
static inline void console_srcu_write_flags(struct console *con, short flags)
|
||||
{
|
||||
lockdep_assert_console_list_lock_held();
|
||||
|
||||
/* This matches the READ_ONCE() in console_srcu_read_flags(). */
|
||||
WRITE_ONCE(con->flags, flags);
|
||||
}
|
||||
|
||||
/* Variant of console_is_registered() when the console_list_lock is held. */
|
||||
static inline bool console_is_registered_locked(const struct console *con)
|
||||
{
|
||||
lockdep_assert_console_list_lock_held();
|
||||
return !hlist_unhashed(&con->node);
|
||||
}
|
||||
|
||||
/*
|
||||
* console_is_registered - Check if the console is registered
|
||||
* @con: struct console pointer of console to check
|
||||
*
|
||||
* Context: Process context. May sleep while acquiring console list lock.
|
||||
* Return: true if the console is in the console list, otherwise false.
|
||||
*
|
||||
* If false is returned for a console that was previously registered, it
|
||||
* can be assumed that the console's unregistration is fully completed,
|
||||
* including the exit() callback after console list removal.
|
||||
*/
|
||||
static inline bool console_is_registered(const struct console *con)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
console_list_lock();
|
||||
ret = console_is_registered_locked(con);
|
||||
console_list_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* for_each_console_srcu() - Iterator over registered consoles
|
||||
* @con: struct console pointer used as loop cursor
|
||||
*
|
||||
* Although SRCU guarantees the console list will be consistent, the
|
||||
* struct console fields may be updated by other CPUs while iterating.
|
||||
*
|
||||
* Requires console_srcu_read_lock to be held. Can be invoked from
|
||||
* any context.
|
||||
*/
|
||||
#define for_each_console_srcu(con) \
|
||||
hlist_for_each_entry_srcu(con, &console_list, node, \
|
||||
console_srcu_read_lock_is_held())
|
||||
|
||||
/**
|
||||
* for_each_console() - Iterator over registered consoles
|
||||
* @con: struct console pointer used as loop cursor
|
||||
*
|
||||
* The console list and the console->flags are immutable while iterating.
|
||||
*
|
||||
* Requires console_list_lock to be held.
|
||||
*/
|
||||
#define for_each_console(con) \
|
||||
lockdep_assert_console_list_lock_held(); \
|
||||
hlist_for_each_entry(con, &console_list, node)
|
||||
|
||||
extern int console_set_on_cmdline;
|
||||
extern struct console *early_console;
|
||||
@ -172,9 +291,9 @@ enum con_flush_mode {
|
||||
};
|
||||
|
||||
extern int add_preferred_console(char *name, int idx, char *options);
|
||||
extern void console_force_preferred_locked(struct console *con);
|
||||
extern void register_console(struct console *);
|
||||
extern int unregister_console(struct console *);
|
||||
extern struct console *console_drivers;
|
||||
extern void console_lock(void);
|
||||
extern int console_trylock(void);
|
||||
extern void console_unlock(void);
|
||||
|
@ -152,11 +152,7 @@ static inline bool rcu_preempt_need_deferred_qs(struct task_struct *t)
|
||||
return false;
|
||||
}
|
||||
static inline void rcu_preempt_deferred_qs(struct task_struct *t) { }
|
||||
#ifdef CONFIG_SRCU
|
||||
void rcu_scheduler_starting(void);
|
||||
#else /* #ifndef CONFIG_SRCU */
|
||||
static inline void rcu_scheduler_starting(void) { }
|
||||
#endif /* #else #ifndef CONFIG_SRCU */
|
||||
static inline void rcu_end_inkernel_boot(void) { }
|
||||
static inline bool rcu_inkernel_boot_has_ended(void) { return true; }
|
||||
static inline bool rcu_is_watching(void) { return true; }
|
||||
|
@ -743,9 +743,15 @@ static const bool earlycon_acpi_spcr_enable EARLYCON_USED_OR_UNUSED;
|
||||
static inline int setup_earlycon(char *buf) { return 0; }
|
||||
#endif
|
||||
|
||||
static inline bool uart_console_enabled(struct uart_port *port)
|
||||
/* Variant of uart_console_registered() when the console_list_lock is held. */
|
||||
static inline bool uart_console_registered_locked(struct uart_port *port)
|
||||
{
|
||||
return uart_console(port) && (port->cons->flags & CON_ENABLED);
|
||||
return uart_console(port) && console_is_registered_locked(port->cons);
|
||||
}
|
||||
|
||||
static inline bool uart_console_registered(struct uart_port *port)
|
||||
{
|
||||
return uart_console(port) && console_is_registered(port->cons);
|
||||
}
|
||||
|
||||
struct uart_port *uart_get_console(struct uart_port *ports, int nr,
|
||||
|
@ -47,11 +47,8 @@ int init_srcu_struct(struct srcu_struct *ssp);
|
||||
#include <linux/srcutiny.h>
|
||||
#elif defined(CONFIG_TREE_SRCU)
|
||||
#include <linux/srcutree.h>
|
||||
#elif defined(CONFIG_SRCU)
|
||||
#error "Unknown SRCU implementation specified to kernel configuration"
|
||||
#else
|
||||
/* Dummy definition for things like notifiers. Actual use gets link error. */
|
||||
struct srcu_struct { };
|
||||
#error "Unknown SRCU implementation specified to kernel configuration"
|
||||
#endif
|
||||
|
||||
void call_srcu(struct srcu_struct *ssp, struct rcu_head *head,
|
||||
@ -78,11 +75,7 @@ static inline void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
|
||||
}
|
||||
#endif /* CONFIG_NEED_SRCU_NMI_SAFE */
|
||||
|
||||
#ifdef CONFIG_SRCU
|
||||
void srcu_init(void);
|
||||
#else /* #ifdef CONFIG_SRCU */
|
||||
static inline void srcu_init(void) { }
|
||||
#endif /* #else #ifdef CONFIG_SRCU */
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
|
||||
|
@ -545,6 +545,7 @@ static void kdb_msg_write(const char *msg, int msg_len)
|
||||
{
|
||||
struct console *c;
|
||||
const char *cp;
|
||||
int cookie;
|
||||
int len;
|
||||
|
||||
if (msg_len == 0)
|
||||
@ -558,8 +559,20 @@ static void kdb_msg_write(const char *msg, int msg_len)
|
||||
cp++;
|
||||
}
|
||||
|
||||
for_each_console(c) {
|
||||
if (!(c->flags & CON_ENABLED))
|
||||
/*
|
||||
* The console_srcu_read_lock() only provides safe console list
|
||||
* traversal. The use of the ->write() callback relies on all other
|
||||
* CPUs being stopped at the moment and console drivers being able to
|
||||
* handle reentrance when @oops_in_progress is set.
|
||||
*
|
||||
* There is no guarantee that every console driver can handle
|
||||
* reentrance in this way; the developer deploying the debugger
|
||||
* is responsible for ensuring that the console drivers they
|
||||
* have selected handle reentrance appropriately.
|
||||
*/
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
if (!(console_srcu_read_flags(c) & CON_ENABLED))
|
||||
continue;
|
||||
if (c == dbg_io_ops->cons)
|
||||
continue;
|
||||
@ -577,6 +590,7 @@ static void kdb_msg_write(const char *msg, int msg_len)
|
||||
--oops_in_progress;
|
||||
touch_nmi_watchdog();
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
}
|
||||
|
||||
int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
|
||||
|
@ -79,13 +79,20 @@ int oops_in_progress;
|
||||
EXPORT_SYMBOL(oops_in_progress);
|
||||
|
||||
/*
|
||||
* console_sem protects the console_drivers list, and also
|
||||
* provides serialisation for access to the entire console
|
||||
* driver system.
|
||||
* console_mutex protects console_list updates and console->flags updates.
|
||||
* The flags are synchronized only for consoles that are registered, i.e.
|
||||
* accessible via the console list.
|
||||
*/
|
||||
static DEFINE_MUTEX(console_mutex);
|
||||
|
||||
/*
|
||||
* console_sem protects updates to console->seq and console_suspended,
|
||||
* and also provides serialization for console printing.
|
||||
*/
|
||||
static DEFINE_SEMAPHORE(console_sem);
|
||||
struct console *console_drivers;
|
||||
EXPORT_SYMBOL_GPL(console_drivers);
|
||||
HLIST_HEAD(console_list);
|
||||
EXPORT_SYMBOL_GPL(console_list);
|
||||
DEFINE_STATIC_SRCU(console_srcu);
|
||||
|
||||
/*
|
||||
* System may need to suppress printk message under certain
|
||||
@ -103,6 +110,19 @@ static int __read_mostly suppress_panic_printk;
|
||||
static struct lockdep_map console_lock_dep_map = {
|
||||
.name = "console_lock"
|
||||
};
|
||||
|
||||
void lockdep_assert_console_list_lock_held(void)
|
||||
{
|
||||
lockdep_assert_held(&console_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(lockdep_assert_console_list_lock_held);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
bool console_srcu_read_lock_is_held(void)
|
||||
{
|
||||
return srcu_read_lock_held(&console_srcu);
|
||||
}
|
||||
#endif
|
||||
|
||||
enum devkmsg_log_bits {
|
||||
@ -220,6 +240,69 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
|
||||
}
|
||||
#endif /* CONFIG_PRINTK && CONFIG_SYSCTL */
|
||||
|
||||
/**
|
||||
* console_list_lock - Lock the console list
|
||||
*
|
||||
* For console list or console->flags updates
|
||||
*/
|
||||
void console_list_lock(void)
|
||||
{
|
||||
/*
|
||||
* In unregister_console() and console_force_preferred_locked(),
|
||||
* synchronize_srcu() is called with the console_list_lock held.
|
||||
* Therefore it is not allowed that the console_list_lock is taken
|
||||
* with the srcu_lock held.
|
||||
*
|
||||
* Detecting if this context is really in the read-side critical
|
||||
* section is only possible if the appropriate debug options are
|
||||
* enabled.
|
||||
*/
|
||||
WARN_ON_ONCE(debug_lockdep_rcu_enabled() &&
|
||||
srcu_read_lock_held(&console_srcu));
|
||||
|
||||
mutex_lock(&console_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(console_list_lock);
|
||||
|
||||
/**
|
||||
* console_list_unlock - Unlock the console list
|
||||
*
|
||||
* Counterpart to console_list_lock()
|
||||
*/
|
||||
void console_list_unlock(void)
|
||||
{
|
||||
mutex_unlock(&console_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(console_list_unlock);
|
||||
|
||||
/**
|
||||
* console_srcu_read_lock - Register a new reader for the
|
||||
* SRCU-protected console list
|
||||
*
|
||||
* Use for_each_console_srcu() to iterate the console list
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: A cookie to pass to console_srcu_read_unlock().
|
||||
*/
|
||||
int console_srcu_read_lock(void)
|
||||
{
|
||||
return srcu_read_lock_nmisafe(&console_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL(console_srcu_read_lock);
|
||||
|
||||
/**
|
||||
* console_srcu_read_unlock - Unregister an old reader from
|
||||
* the SRCU-protected console list
|
||||
* @cookie: cookie returned from console_srcu_read_lock()
|
||||
*
|
||||
* Counterpart to console_srcu_read_lock()
|
||||
*/
|
||||
void console_srcu_read_unlock(int cookie)
|
||||
{
|
||||
srcu_read_unlock_nmisafe(&console_srcu, cookie);
|
||||
}
|
||||
EXPORT_SYMBOL(console_srcu_read_unlock);
|
||||
|
||||
/*
|
||||
* Helper macros to handle lockdep when locking/unlocking console_sem. We use
|
||||
* macros instead of functions so that _RET_IP_ contains useful information.
|
||||
@ -1814,13 +1897,13 @@ static void console_lock_spinning_enable(void)
|
||||
* safe to start busy waiting for the lock. Second, it checks if
|
||||
* there is a busy waiter and passes the lock rights to her.
|
||||
*
|
||||
* Important: Callers lose the lock if there was a busy waiter.
|
||||
* They must not touch items synchronized by console_lock
|
||||
* in this case.
|
||||
* Important: Callers lose both the console_lock and the SRCU read lock if
|
||||
* there was a busy waiter. They must not touch items synchronized by
|
||||
* console_lock or SRCU read lock in this case.
|
||||
*
|
||||
* Return: 1 if the lock rights were passed, 0 otherwise.
|
||||
*/
|
||||
static int console_lock_spinning_disable_and_check(void)
|
||||
static int console_lock_spinning_disable_and_check(int cookie)
|
||||
{
|
||||
int waiter;
|
||||
|
||||
@ -1839,6 +1922,12 @@ static int console_lock_spinning_disable_and_check(void)
|
||||
|
||||
spin_release(&console_owner_dep_map, _THIS_IP_);
|
||||
|
||||
/*
|
||||
* Preserve lockdep lock ordering. Release the SRCU read lock before
|
||||
* releasing the console_lock.
|
||||
*/
|
||||
console_srcu_read_unlock(cookie);
|
||||
|
||||
/*
|
||||
* Hand off console_lock to waiter. The waiter will perform
|
||||
* the up(). After this, the waiter is the console_lock owner.
|
||||
@ -2322,7 +2411,7 @@ static ssize_t msg_print_ext_body(char *buf, size_t size,
|
||||
char *text, size_t text_len,
|
||||
struct dev_printk_info *dev_info) { return 0; }
|
||||
static void console_lock_spinning_enable(void) { }
|
||||
static int console_lock_spinning_disable_and_check(void) { return 0; }
|
||||
static int console_lock_spinning_disable_and_check(int cookie) { return 0; }
|
||||
static void call_console_driver(struct console *con, const char *text, size_t len,
|
||||
char *dropped_text)
|
||||
{
|
||||
@ -2391,7 +2480,7 @@ static int __add_preferred_console(char *name, int idx, char *options,
|
||||
return -E2BIG;
|
||||
if (!brl_options)
|
||||
preferred_console = i;
|
||||
strlcpy(c->name, name, sizeof(c->name));
|
||||
strscpy(c->name, name, sizeof(c->name));
|
||||
c->options = options;
|
||||
set_user_specified(c, user_specified);
|
||||
braille_set_options(c, brl_options);
|
||||
@ -2553,10 +2642,10 @@ static int console_cpu_notify(unsigned int cpu)
|
||||
}
|
||||
|
||||
/**
|
||||
* console_lock - lock the console system for exclusive use.
|
||||
* console_lock - block the console subsystem from printing
|
||||
*
|
||||
* Acquires a lock which guarantees that the caller has
|
||||
* exclusive access to the console system and the console_drivers list.
|
||||
* Acquires a lock which guarantees that no consoles will
|
||||
* be in or enter their write() callback.
|
||||
*
|
||||
* Can sleep, returns nothing.
|
||||
*/
|
||||
@ -2573,10 +2662,10 @@ void console_lock(void)
|
||||
EXPORT_SYMBOL(console_lock);
|
||||
|
||||
/**
|
||||
* console_trylock - try to lock the console system for exclusive use.
|
||||
* console_trylock - try to block the console subsystem from printing
|
||||
*
|
||||
* Try to acquire a lock which guarantees that the caller has exclusive
|
||||
* access to the console system and the console_drivers list.
|
||||
* Try to acquire a lock which guarantees that no consoles will
|
||||
* be in or enter their write() callback.
|
||||
*
|
||||
* returns 1 on success, and 0 on failure to acquire the lock.
|
||||
*/
|
||||
@ -2623,11 +2712,13 @@ static bool abandon_console_lock_in_panic(void)
|
||||
* Check if the given console is currently capable and allowed to print
|
||||
* records.
|
||||
*
|
||||
* Requires the console_lock.
|
||||
* Requires the console_srcu_read_lock.
|
||||
*/
|
||||
static inline bool console_is_usable(struct console *con)
|
||||
{
|
||||
if (!(con->flags & CON_ENABLED))
|
||||
short flags = console_srcu_read_flags(con);
|
||||
|
||||
if (!(flags & CON_ENABLED))
|
||||
return false;
|
||||
|
||||
if (!con->write)
|
||||
@ -2638,8 +2729,7 @@ static inline bool console_is_usable(struct console *con)
|
||||
* allocated. So unless they're explicitly marked as being able to
|
||||
* cope (CON_ANYTIME) don't call them until this CPU is officially up.
|
||||
*/
|
||||
if (!cpu_online(raw_smp_processor_id()) &&
|
||||
!(con->flags & CON_ANYTIME))
|
||||
if (!cpu_online(raw_smp_processor_id()) && !(flags & CON_ANYTIME))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -2664,16 +2754,18 @@ static void __console_unlock(void)
|
||||
* DROPPED_TEXT_MAX. Otherwise @dropped_text must be NULL.
|
||||
*
|
||||
* @handover will be set to true if a printk waiter has taken over the
|
||||
* console_lock, in which case the caller is no longer holding the
|
||||
* console_lock. Otherwise it is set to false.
|
||||
* console_lock, in which case the caller is no longer holding both the
|
||||
* console_lock and the SRCU read lock. Otherwise it is set to false.
|
||||
*
|
||||
* @cookie is the cookie from the SRCU read lock.
|
||||
*
|
||||
* Returns false if the given console has no next record to print, otherwise
|
||||
* true.
|
||||
*
|
||||
* Requires the console_lock.
|
||||
* Requires the console_lock and the SRCU read lock.
|
||||
*/
|
||||
static bool console_emit_next_record(struct console *con, char *text, char *ext_text,
|
||||
char *dropped_text, bool *handover)
|
||||
char *dropped_text, bool *handover, int cookie)
|
||||
{
|
||||
static int panic_console_dropped;
|
||||
struct printk_info info;
|
||||
@ -2733,7 +2825,7 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_
|
||||
|
||||
con->seq++;
|
||||
|
||||
*handover = console_lock_spinning_disable_and_check();
|
||||
*handover = console_lock_spinning_disable_and_check(cookie);
|
||||
printk_safe_exit_irqrestore(flags);
|
||||
skip:
|
||||
return true;
|
||||
@ -2770,6 +2862,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove
|
||||
bool any_usable = false;
|
||||
struct console *con;
|
||||
bool any_progress;
|
||||
int cookie;
|
||||
|
||||
*next_seq = 0;
|
||||
*handover = false;
|
||||
@ -2777,23 +2870,29 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove
|
||||
do {
|
||||
any_progress = false;
|
||||
|
||||
for_each_console(con) {
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(con) {
|
||||
bool progress;
|
||||
|
||||
if (!console_is_usable(con))
|
||||
continue;
|
||||
any_usable = true;
|
||||
|
||||
if (con->flags & CON_EXTENDED) {
|
||||
if (console_srcu_read_flags(con) & CON_EXTENDED) {
|
||||
/* Extended consoles do not print "dropped messages". */
|
||||
progress = console_emit_next_record(con, &text[0],
|
||||
&ext_text[0], NULL,
|
||||
handover);
|
||||
handover, cookie);
|
||||
} else {
|
||||
progress = console_emit_next_record(con, &text[0],
|
||||
NULL, &dropped_text[0],
|
||||
handover);
|
||||
handover, cookie);
|
||||
}
|
||||
|
||||
/*
|
||||
* If a handover has occurred, the SRCU read lock
|
||||
* is already released.
|
||||
*/
|
||||
if (*handover)
|
||||
return false;
|
||||
|
||||
@ -2807,21 +2906,26 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove
|
||||
|
||||
/* Allow panic_cpu to take over the consoles safely. */
|
||||
if (abandon_console_lock_in_panic())
|
||||
return false;
|
||||
goto abandon;
|
||||
|
||||
if (do_cond_resched)
|
||||
cond_resched();
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
} while (any_progress);
|
||||
|
||||
return any_usable;
|
||||
|
||||
abandon:
|
||||
console_srcu_read_unlock(cookie);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* console_unlock - unlock the console system
|
||||
* console_unlock - unblock the console subsystem from printing
|
||||
*
|
||||
* Releases the console_lock which the caller holds on the console system
|
||||
* and the console driver list.
|
||||
* Releases the console_lock which the caller holds to block printing of
|
||||
* the console subsystem.
|
||||
*
|
||||
* While the console_lock was held, console output may have been buffered
|
||||
* by printk(). If this is the case, console_unlock(); emits
|
||||
@ -2899,10 +3003,14 @@ EXPORT_SYMBOL(console_conditional_schedule);
|
||||
void console_unblank(void)
|
||||
{
|
||||
struct console *c;
|
||||
int cookie;
|
||||
|
||||
/*
|
||||
* console_unblank can no longer be called in interrupt context unless
|
||||
* oops_in_progress is set to 1..
|
||||
* Stop console printing because the unblank() callback may
|
||||
* assume the console is not within its write() callback.
|
||||
*
|
||||
* If @oops_in_progress is set, this may be an atomic context.
|
||||
* In that case, attempt a trylock as best-effort.
|
||||
*/
|
||||
if (oops_in_progress) {
|
||||
if (down_trylock_console_sem() != 0)
|
||||
@ -2912,9 +3020,14 @@ void console_unblank(void)
|
||||
|
||||
console_locked = 1;
|
||||
console_may_schedule = 0;
|
||||
for_each_console(c)
|
||||
if ((c->flags & CON_ENABLED) && c->unblank)
|
||||
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
if ((console_srcu_read_flags(c) & CON_ENABLED) && c->unblank)
|
||||
c->unblank();
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
|
||||
console_unlock();
|
||||
|
||||
if (!oops_in_progress)
|
||||
@ -2941,11 +3054,21 @@ void console_flush_on_panic(enum con_flush_mode mode)
|
||||
|
||||
if (mode == CONSOLE_REPLAY_ALL) {
|
||||
struct console *c;
|
||||
int cookie;
|
||||
u64 seq;
|
||||
|
||||
seq = prb_first_valid_seq(prb);
|
||||
for_each_console(c)
|
||||
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
/*
|
||||
* If the above console_trylock() failed, this is an
|
||||
* unsynchronized assignment. But in that case, the
|
||||
* kernel is in "hope and pray" mode anyway.
|
||||
*/
|
||||
c->seq = seq;
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
}
|
||||
console_unlock();
|
||||
}
|
||||
@ -2957,15 +3080,25 @@ struct tty_driver *console_device(int *index)
|
||||
{
|
||||
struct console *c;
|
||||
struct tty_driver *driver = NULL;
|
||||
int cookie;
|
||||
|
||||
/*
|
||||
* 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(c) {
|
||||
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
if (!c->device)
|
||||
continue;
|
||||
driver = c->device(c, index);
|
||||
if (driver)
|
||||
break;
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
|
||||
console_unlock();
|
||||
return driver;
|
||||
}
|
||||
@ -2978,17 +3111,25 @@ struct tty_driver *console_device(int *index)
|
||||
void console_stop(struct console *console)
|
||||
{
|
||||
__pr_flush(console, 1000, true);
|
||||
console_lock();
|
||||
console->flags &= ~CON_ENABLED;
|
||||
console_unlock();
|
||||
console_list_lock();
|
||||
console_srcu_write_flags(console, console->flags & ~CON_ENABLED);
|
||||
console_list_unlock();
|
||||
|
||||
/*
|
||||
* Ensure that all SRCU list walks have completed. All contexts must
|
||||
* be able to see that this console is disabled so that (for example)
|
||||
* the caller can suspend the port without risk of another context
|
||||
* using the port.
|
||||
*/
|
||||
synchronize_srcu(&console_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL(console_stop);
|
||||
|
||||
void console_start(struct console *console)
|
||||
{
|
||||
console_lock();
|
||||
console->flags |= CON_ENABLED;
|
||||
console_unlock();
|
||||
console_list_lock();
|
||||
console_srcu_write_flags(console, console->flags | CON_ENABLED);
|
||||
console_list_unlock();
|
||||
__pr_flush(console, 1000, true);
|
||||
}
|
||||
EXPORT_SYMBOL(console_start);
|
||||
@ -3081,6 +3222,72 @@ static void try_enable_default_console(struct console *newcon)
|
||||
(con->flags & CON_BOOT) ? "boot" : "", \
|
||||
con->name, con->index, ##__VA_ARGS__)
|
||||
|
||||
static void console_init_seq(struct console *newcon, bool bootcon_registered)
|
||||
{
|
||||
struct console *con;
|
||||
bool handover;
|
||||
|
||||
if (newcon->flags & (CON_PRINTBUFFER | CON_BOOT)) {
|
||||
/* Get a consistent copy of @syslog_seq. */
|
||||
mutex_lock(&syslog_lock);
|
||||
newcon->seq = syslog_seq;
|
||||
mutex_unlock(&syslog_lock);
|
||||
} else {
|
||||
/* Begin with next message added to ringbuffer. */
|
||||
newcon->seq = prb_next_seq(prb);
|
||||
|
||||
/*
|
||||
* If any enabled boot consoles are due to be unregistered
|
||||
* shortly, some may not be caught up and may be the same
|
||||
* device as @newcon. Since it is not known which boot console
|
||||
* is the same device, flush all consoles and, if necessary,
|
||||
* start with the message of the enabled boot console that is
|
||||
* the furthest behind.
|
||||
*/
|
||||
if (bootcon_registered && !keep_bootcon) {
|
||||
/*
|
||||
* Hold the console_lock to stop console printing and
|
||||
* guarantee safe access to console->seq.
|
||||
*/
|
||||
console_lock();
|
||||
|
||||
/*
|
||||
* Flush all consoles and set the console to start at
|
||||
* the next unprinted sequence number.
|
||||
*/
|
||||
if (!console_flush_all(true, &newcon->seq, &handover)) {
|
||||
/*
|
||||
* Flushing failed. Just choose the lowest
|
||||
* sequence of the enabled boot consoles.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If there was a handover, this context no
|
||||
* longer holds the console_lock.
|
||||
*/
|
||||
if (handover)
|
||||
console_lock();
|
||||
|
||||
newcon->seq = prb_next_seq(prb);
|
||||
for_each_console(con) {
|
||||
if ((con->flags & CON_BOOT) &&
|
||||
(con->flags & CON_ENABLED) &&
|
||||
con->seq < newcon->seq) {
|
||||
newcon->seq = con->seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console_unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define console_first() \
|
||||
hlist_entry(console_list.first, struct console, node)
|
||||
|
||||
static int unregister_console_locked(struct console *console);
|
||||
|
||||
/*
|
||||
* The console driver calls this routine during kernel initialization
|
||||
* to register the console printing procedure with printk() and to
|
||||
@ -3103,28 +3310,29 @@ static void try_enable_default_console(struct console *newcon)
|
||||
void register_console(struct console *newcon)
|
||||
{
|
||||
struct console *con;
|
||||
bool bootcon_enabled = false;
|
||||
bool realcon_enabled = false;
|
||||
bool bootcon_registered = false;
|
||||
bool realcon_registered = false;
|
||||
int err;
|
||||
|
||||
console_list_lock();
|
||||
|
||||
for_each_console(con) {
|
||||
if (WARN(con == newcon, "console '%s%d' already registered\n",
|
||||
con->name, con->index))
|
||||
return;
|
||||
}
|
||||
con->name, con->index)) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
for_each_console(con) {
|
||||
if (con->flags & CON_BOOT)
|
||||
bootcon_enabled = true;
|
||||
bootcon_registered = true;
|
||||
else
|
||||
realcon_enabled = true;
|
||||
realcon_registered = true;
|
||||
}
|
||||
|
||||
/* Do not register boot consoles when there already is a real one. */
|
||||
if (newcon->flags & CON_BOOT && realcon_enabled) {
|
||||
if ((newcon->flags & CON_BOOT) && realcon_registered) {
|
||||
pr_info("Too late to register bootconsole %s%d\n",
|
||||
newcon->name, newcon->index);
|
||||
return;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3140,8 +3348,8 @@ void register_console(struct console *newcon)
|
||||
* flag set and will be first in the list.
|
||||
*/
|
||||
if (preferred_console < 0) {
|
||||
if (!console_drivers || !console_drivers->device ||
|
||||
console_drivers->flags & CON_BOOT) {
|
||||
if (hlist_empty(&console_list) || !console_first()->device ||
|
||||
console_first()->flags & CON_BOOT) {
|
||||
try_enable_default_console(newcon);
|
||||
}
|
||||
}
|
||||
@ -3155,7 +3363,7 @@ void register_console(struct console *newcon)
|
||||
|
||||
/* printk() messages are not printed to the Braille console. */
|
||||
if (err || newcon->flags & CON_BRL)
|
||||
return;
|
||||
goto unlock;
|
||||
|
||||
/*
|
||||
* If we have a bootconsole, and are switching to a real console,
|
||||
@ -3163,39 +3371,38 @@ void register_console(struct console *newcon)
|
||||
* the real console are the same physical device, it's annoying to
|
||||
* see the beginning boot messages twice
|
||||
*/
|
||||
if (bootcon_enabled &&
|
||||
if (bootcon_registered &&
|
||||
((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV)) {
|
||||
newcon->flags &= ~CON_PRINTBUFFER;
|
||||
}
|
||||
|
||||
newcon->dropped = 0;
|
||||
console_init_seq(newcon, bootcon_registered);
|
||||
|
||||
/*
|
||||
* Put this console in the list - keep the
|
||||
* preferred driver at the head of the list.
|
||||
* Put this console in the list - keep the
|
||||
* preferred driver at the head of the list.
|
||||
*/
|
||||
console_lock();
|
||||
if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
|
||||
newcon->next = console_drivers;
|
||||
console_drivers = newcon;
|
||||
if (newcon->next)
|
||||
newcon->next->flags &= ~CON_CONSDEV;
|
||||
/* Ensure this flag is always set for the head of the list */
|
||||
if (hlist_empty(&console_list)) {
|
||||
/* Ensure CON_CONSDEV is always set for the head. */
|
||||
newcon->flags |= CON_CONSDEV;
|
||||
hlist_add_head_rcu(&newcon->node, &console_list);
|
||||
|
||||
} else if (newcon->flags & CON_CONSDEV) {
|
||||
/* Only the new head can have CON_CONSDEV set. */
|
||||
console_srcu_write_flags(console_first(), console_first()->flags & ~CON_CONSDEV);
|
||||
hlist_add_head_rcu(&newcon->node, &console_list);
|
||||
|
||||
} else {
|
||||
newcon->next = console_drivers->next;
|
||||
console_drivers->next = newcon;
|
||||
hlist_add_behind_rcu(&newcon->node, console_list.first);
|
||||
}
|
||||
|
||||
newcon->dropped = 0;
|
||||
if (newcon->flags & CON_PRINTBUFFER) {
|
||||
/* Get a consistent copy of @syslog_seq. */
|
||||
mutex_lock(&syslog_lock);
|
||||
newcon->seq = syslog_seq;
|
||||
mutex_unlock(&syslog_lock);
|
||||
} else {
|
||||
/* Begin with next message. */
|
||||
newcon->seq = prb_next_seq(prb);
|
||||
}
|
||||
console_unlock();
|
||||
/*
|
||||
* No need to synchronize SRCU here! The caller does not rely
|
||||
* on all contexts being able to see the new console before
|
||||
* register_console() completes.
|
||||
*/
|
||||
|
||||
console_sysfs_notify();
|
||||
|
||||
/*
|
||||
@ -3206,21 +3413,28 @@ void register_console(struct console *newcon)
|
||||
* went to the bootconsole (that they do not see on the real console)
|
||||
*/
|
||||
con_printk(KERN_INFO, newcon, "enabled\n");
|
||||
if (bootcon_enabled &&
|
||||
if (bootcon_registered &&
|
||||
((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
|
||||
!keep_bootcon) {
|
||||
for_each_console(con)
|
||||
struct hlist_node *tmp;
|
||||
|
||||
hlist_for_each_entry_safe(con, tmp, &console_list, node) {
|
||||
if (con->flags & CON_BOOT)
|
||||
unregister_console(con);
|
||||
unregister_console_locked(con);
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
console_list_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL(register_console);
|
||||
|
||||
int unregister_console(struct console *console)
|
||||
/* Must be called under console_list_lock(). */
|
||||
static int unregister_console_locked(struct console *console)
|
||||
{
|
||||
struct console *con;
|
||||
int res;
|
||||
|
||||
lockdep_assert_console_list_lock_held();
|
||||
|
||||
con_printk(KERN_INFO, console, "disabled\n");
|
||||
|
||||
res = _braille_unregister_console(console);
|
||||
@ -3229,48 +3443,94 @@ int unregister_console(struct console *console)
|
||||
if (res > 0)
|
||||
return 0;
|
||||
|
||||
res = -ENODEV;
|
||||
console_lock();
|
||||
if (console_drivers == console) {
|
||||
console_drivers=console->next;
|
||||
res = 0;
|
||||
} else {
|
||||
for_each_console(con) {
|
||||
if (con->next == console) {
|
||||
con->next = console->next;
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Disable it unconditionally */
|
||||
console_srcu_write_flags(console, console->flags & ~CON_ENABLED);
|
||||
|
||||
if (res)
|
||||
goto out_disable_unlock;
|
||||
if (!console_is_registered_locked(console))
|
||||
return -ENODEV;
|
||||
|
||||
hlist_del_init_rcu(&console->node);
|
||||
|
||||
/*
|
||||
* <HISTORICAL>
|
||||
* If this isn't the last console and it has CON_CONSDEV set, we
|
||||
* need to set it on the next preferred console.
|
||||
* </HISTORICAL>
|
||||
*
|
||||
* The above makes no sense as there is no guarantee that the next
|
||||
* console has any device attached. Oh well....
|
||||
*/
|
||||
if (console_drivers != NULL && console->flags & CON_CONSDEV)
|
||||
console_drivers->flags |= CON_CONSDEV;
|
||||
if (!hlist_empty(&console_list) && console->flags & CON_CONSDEV)
|
||||
console_srcu_write_flags(console_first(), console_first()->flags | CON_CONSDEV);
|
||||
|
||||
/*
|
||||
* Ensure that all SRCU list walks have completed. All contexts
|
||||
* must not be able to see this console in the list so that any
|
||||
* exit/cleanup routines can be performed safely.
|
||||
*/
|
||||
synchronize_srcu(&console_srcu);
|
||||
|
||||
console->flags &= ~CON_ENABLED;
|
||||
console_unlock();
|
||||
console_sysfs_notify();
|
||||
|
||||
if (console->exit)
|
||||
res = console->exit(console);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
out_disable_unlock:
|
||||
console->flags &= ~CON_ENABLED;
|
||||
console_unlock();
|
||||
int unregister_console(struct console *console)
|
||||
{
|
||||
int res;
|
||||
|
||||
console_list_lock();
|
||||
res = unregister_console_locked(console);
|
||||
console_list_unlock();
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_console);
|
||||
|
||||
/**
|
||||
* console_force_preferred_locked - force a registered console preferred
|
||||
* @con: The registered console to force preferred.
|
||||
*
|
||||
* Must be called under console_list_lock().
|
||||
*/
|
||||
void console_force_preferred_locked(struct console *con)
|
||||
{
|
||||
struct console *cur_pref_con;
|
||||
|
||||
if (!console_is_registered_locked(con))
|
||||
return;
|
||||
|
||||
cur_pref_con = console_first();
|
||||
|
||||
/* Already preferred? */
|
||||
if (cur_pref_con == con)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Delete, but do not re-initialize the entry. This allows the console
|
||||
* to continue to appear registered (via any hlist_unhashed_lockless()
|
||||
* checks), even though it was briefly removed from the console list.
|
||||
*/
|
||||
hlist_del_rcu(&con->node);
|
||||
|
||||
/*
|
||||
* Ensure that all SRCU list walks have completed so that the console
|
||||
* can be added to the beginning of the console list and its forward
|
||||
* list pointer can be re-initialized.
|
||||
*/
|
||||
synchronize_srcu(&console_srcu);
|
||||
|
||||
con->flags |= CON_CONSDEV;
|
||||
WARN_ON(!con->device);
|
||||
|
||||
/* Only the new head can have CON_CONSDEV set. */
|
||||
console_srcu_write_flags(cur_pref_con, cur_pref_con->flags & ~CON_CONSDEV);
|
||||
hlist_add_head_rcu(&con->node, &console_list);
|
||||
}
|
||||
EXPORT_SYMBOL(console_force_preferred_locked);
|
||||
|
||||
/*
|
||||
* Initialize the console device. This is called *early*, so
|
||||
* we can't necessarily depend on lots of kernel help here.
|
||||
@ -3317,10 +3577,12 @@ void __init console_init(void)
|
||||
*/
|
||||
static int __init printk_late_init(void)
|
||||
{
|
||||
struct hlist_node *tmp;
|
||||
struct console *con;
|
||||
int ret;
|
||||
|
||||
for_each_console(con) {
|
||||
console_list_lock();
|
||||
hlist_for_each_entry_safe(con, tmp, &console_list, node) {
|
||||
if (!(con->flags & CON_BOOT))
|
||||
continue;
|
||||
|
||||
@ -3337,9 +3599,11 @@ static int __init printk_late_init(void)
|
||||
*/
|
||||
pr_warn("bootconsole [%s%d] uses init memory and must be disabled even before the real one is ready\n",
|
||||
con->name, con->index);
|
||||
unregister_console(con);
|
||||
unregister_console_locked(con);
|
||||
}
|
||||
}
|
||||
console_list_unlock();
|
||||
|
||||
ret = cpuhp_setup_state_nocalls(CPUHP_PRINTK_DEAD, "printk:dead", NULL,
|
||||
console_cpu_notify);
|
||||
WARN_ON(ret < 0);
|
||||
@ -3359,6 +3623,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
|
||||
struct console *c;
|
||||
u64 last_diff = 0;
|
||||
u64 printk_seq;
|
||||
int cookie;
|
||||
u64 diff;
|
||||
u64 seq;
|
||||
|
||||
@ -3369,9 +3634,15 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
|
||||
for (;;) {
|
||||
diff = 0;
|
||||
|
||||
/*
|
||||
* Hold the console_lock to guarantee safe access to
|
||||
* console->seq and to prevent changes to @console_suspended
|
||||
* until all consoles have been processed.
|
||||
*/
|
||||
console_lock();
|
||||
|
||||
for_each_console(c) {
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
if (con && con != c)
|
||||
continue;
|
||||
if (!console_is_usable(c))
|
||||
@ -3380,6 +3651,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
|
||||
if (printk_seq < seq)
|
||||
diff += seq - printk_seq;
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
|
||||
/*
|
||||
* If consoles are suspended, it cannot be expected that they
|
||||
|
@ -203,7 +203,7 @@
|
||||
* prb_rec_init_wr(&r, 5);
|
||||
*
|
||||
* // try to extend, but only if it does not exceed 32 bytes
|
||||
* if (prb_reserve_in_last(&e, &test_rb, &r, printk_caller_id()), 32) {
|
||||
* if (prb_reserve_in_last(&e, &test_rb, &r, printk_caller_id(), 32)) {
|
||||
* snprintf(&r.text_buf[r.info->text_len],
|
||||
* r.text_buf_size - r.info->text_len, "hello");
|
||||
*
|
||||
|
@ -54,21 +54,17 @@ config RCU_EXPERT
|
||||
Say N if you are unsure.
|
||||
|
||||
config SRCU
|
||||
bool
|
||||
help
|
||||
This option selects the sleepable version of RCU. This version
|
||||
permits arbitrary sleeping or blocking within RCU read-side critical
|
||||
sections.
|
||||
def_bool y
|
||||
|
||||
config TINY_SRCU
|
||||
bool
|
||||
default y if SRCU && TINY_RCU
|
||||
default y if TINY_RCU
|
||||
help
|
||||
This option selects the single-CPU non-preemptible version of SRCU.
|
||||
|
||||
config TREE_SRCU
|
||||
bool
|
||||
default y if SRCU && !TINY_RCU
|
||||
default y if !TINY_RCU
|
||||
help
|
||||
This option selects the full-fledged version of SRCU.
|
||||
|
||||
@ -77,7 +73,6 @@ config NEED_SRCU_NMI_SAFE
|
||||
|
||||
config TASKS_RCU_GENERIC
|
||||
def_bool TASKS_RCU || TASKS_RUDE_RCU || TASKS_TRACE_RCU
|
||||
select SRCU
|
||||
help
|
||||
This option enables generic infrastructure code supporting
|
||||
task-based RCU implementations. Not for manual selection.
|
||||
|
@ -27,7 +27,6 @@ config RCU_SCALE_TEST
|
||||
tristate "performance tests for RCU"
|
||||
depends on DEBUG_KERNEL
|
||||
select TORTURE_TEST
|
||||
select SRCU
|
||||
default n
|
||||
help
|
||||
This option provides a kernel module that runs performance
|
||||
@ -43,7 +42,6 @@ config RCU_TORTURE_TEST
|
||||
tristate "torture tests for RCU"
|
||||
depends on DEBUG_KERNEL
|
||||
select TORTURE_TEST
|
||||
select SRCU
|
||||
default n
|
||||
help
|
||||
This option provides a kernel module that runs torture tests
|
||||
@ -59,7 +57,6 @@ config RCU_REF_SCALE_TEST
|
||||
tristate "Scalability tests for read-side synchronization (RCU and others)"
|
||||
depends on DEBUG_KERNEL
|
||||
select TORTURE_TEST
|
||||
select SRCU
|
||||
default n
|
||||
help
|
||||
This option provides a kernel module that runs performance tests
|
||||
|
@ -286,7 +286,7 @@ void rcu_test_sync_prims(void);
|
||||
*/
|
||||
extern void resched_cpu(int cpu);
|
||||
|
||||
#if defined(CONFIG_SRCU) || !defined(CONFIG_TINY_RCU)
|
||||
#if !defined(CONFIG_TINY_RCU)
|
||||
|
||||
#include <linux/rcu_node_tree.h>
|
||||
|
||||
@ -375,6 +375,10 @@ extern void rcu_init_geometry(void);
|
||||
(cpu) <= rnp->grphi; \
|
||||
(cpu) = rcu_find_next_bit((rnp), (cpu) + 1 - (rnp->grplo), (mask)))
|
||||
|
||||
#endif /* !defined(CONFIG_TINY_RCU) */
|
||||
|
||||
#if !defined(CONFIG_TINY_RCU) || defined(CONFIG_TASKS_RCU_GENERIC)
|
||||
|
||||
/*
|
||||
* Wrappers for the rcu_node::lock acquire and release.
|
||||
*
|
||||
@ -437,7 +441,7 @@ do { \
|
||||
#define raw_lockdep_assert_held_rcu_node(p) \
|
||||
lockdep_assert_held(&ACCESS_PRIVATE(p, lock))
|
||||
|
||||
#endif /* #if defined(CONFIG_SRCU) || !defined(CONFIG_TINY_RCU) */
|
||||
#endif // #if !defined(CONFIG_TINY_RCU) || defined(CONFIG_TASKS_RCU_GENERIC)
|
||||
|
||||
#ifdef CONFIG_TINY_RCU
|
||||
/* Tiny RCU doesn't expedite, as its purpose in life is instead to be tiny. */
|
||||
|
@ -197,6 +197,16 @@ void synchronize_srcu(struct srcu_struct *ssp)
|
||||
{
|
||||
struct rcu_synchronize rs;
|
||||
|
||||
RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
|
||||
lock_is_held(&rcu_bh_lock_map) ||
|
||||
lock_is_held(&rcu_lock_map) ||
|
||||
lock_is_held(&rcu_sched_lock_map),
|
||||
"Illegal synchronize_srcu() in same-type SRCU (or in RCU) read-side critical section");
|
||||
|
||||
if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
|
||||
return;
|
||||
|
||||
might_sleep();
|
||||
init_rcu_head_on_stack(&rs.head);
|
||||
init_completion(&rs.completion);
|
||||
call_srcu(ssp, &rs.head, wakeme_after_rcu);
|
||||
|
@ -224,7 +224,7 @@ void rcu_test_sync_prims(void)
|
||||
synchronize_rcu_expedited();
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_TINY_RCU) || defined(CONFIG_SRCU)
|
||||
#if !defined(CONFIG_TINY_RCU)
|
||||
|
||||
/*
|
||||
* Switch to run-time mode once RCU has fully initialized.
|
||||
@ -239,7 +239,7 @@ static int __init rcu_set_runtime_mode(void)
|
||||
}
|
||||
core_initcall(rcu_set_runtime_mode);
|
||||
|
||||
#endif /* #if !defined(CONFIG_TINY_RCU) || defined(CONFIG_SRCU) */
|
||||
#endif /* #if !defined(CONFIG_TINY_RCU) */
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key rcu_lock_key;
|
||||
@ -559,10 +559,8 @@ static void early_boot_test_call_rcu(void)
|
||||
struct early_boot_kfree_rcu *rhp;
|
||||
|
||||
call_rcu(&head, test_callback);
|
||||
if (IS_ENABLED(CONFIG_SRCU)) {
|
||||
early_srcu_cookie = start_poll_synchronize_srcu(&early_srcu);
|
||||
call_srcu(&early_srcu, &shead, test_callback);
|
||||
}
|
||||
early_srcu_cookie = start_poll_synchronize_srcu(&early_srcu);
|
||||
call_srcu(&early_srcu, &shead, test_callback);
|
||||
rhp = kmalloc(sizeof(*rhp), GFP_KERNEL);
|
||||
if (!WARN_ON_ONCE(!rhp))
|
||||
kfree_rcu(rhp, rh);
|
||||
@ -585,11 +583,9 @@ static int rcu_verify_early_boot_tests(void)
|
||||
if (rcu_self_test) {
|
||||
early_boot_test_counter++;
|
||||
rcu_barrier();
|
||||
if (IS_ENABLED(CONFIG_SRCU)) {
|
||||
early_boot_test_counter++;
|
||||
srcu_barrier(&early_srcu);
|
||||
WARN_ON_ONCE(!poll_state_synchronize_srcu(&early_srcu, early_srcu_cookie));
|
||||
}
|
||||
early_boot_test_counter++;
|
||||
srcu_barrier(&early_srcu);
|
||||
WARN_ON_ONCE(!poll_state_synchronize_srcu(&early_srcu, early_srcu_cookie));
|
||||
}
|
||||
if (rcu_self_test_counter != early_boot_test_counter) {
|
||||
WARN_ON(1);
|
||||
|
@ -692,31 +692,29 @@ flags(void)
|
||||
|
||||
static void __init fwnode_pointer(void)
|
||||
{
|
||||
const struct software_node softnodes[] = {
|
||||
{ .name = "first", },
|
||||
{ .name = "second", .parent = &softnodes[0], },
|
||||
{ .name = "third", .parent = &softnodes[1], },
|
||||
{ NULL /* Guardian */ }
|
||||
};
|
||||
const char * const full_name = "first/second/third";
|
||||
const struct software_node first = { .name = "first" };
|
||||
const struct software_node second = { .name = "second", .parent = &first };
|
||||
const struct software_node third = { .name = "third", .parent = &second };
|
||||
const struct software_node *group[] = { &first, &second, &third, NULL };
|
||||
const char * const full_name_second = "first/second";
|
||||
const char * const full_name_third = "first/second/third";
|
||||
const char * const second_name = "second";
|
||||
const char * const third_name = "third";
|
||||
int rval;
|
||||
|
||||
rval = software_node_register_nodes(softnodes);
|
||||
rval = software_node_register_node_group(group);
|
||||
if (rval) {
|
||||
pr_warn("cannot register softnodes; rval %d\n", rval);
|
||||
return;
|
||||
}
|
||||
|
||||
test(full_name_second, "%pfw", software_node_fwnode(&softnodes[1]));
|
||||
test(full_name, "%pfw", software_node_fwnode(&softnodes[2]));
|
||||
test(full_name, "%pfwf", software_node_fwnode(&softnodes[2]));
|
||||
test(second_name, "%pfwP", software_node_fwnode(&softnodes[1]));
|
||||
test(third_name, "%pfwP", software_node_fwnode(&softnodes[2]));
|
||||
test(full_name_second, "%pfw", software_node_fwnode(&second));
|
||||
test(full_name_third, "%pfw", software_node_fwnode(&third));
|
||||
test(full_name_third, "%pfwf", software_node_fwnode(&third));
|
||||
test(second_name, "%pfwP", software_node_fwnode(&second));
|
||||
test(third_name, "%pfwP", software_node_fwnode(&third));
|
||||
|
||||
software_node_unregister_nodes(softnodes);
|
||||
software_node_unregister_node_group(group);
|
||||
}
|
||||
|
||||
static void __init fourcc_pointer(void)
|
||||
|
@ -866,7 +866,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
|
||||
* kptr_restrict==1 cannot be used in IRQ context
|
||||
* because its test for CAP_SYSLOG would be meaningless.
|
||||
*/
|
||||
if (in_irq() || in_serving_softirq() || in_nmi()) {
|
||||
if (in_hardirq() || in_serving_softirq() || in_nmi()) {
|
||||
if (spec.field_width == -1)
|
||||
spec.field_width = 2 * sizeof(ptr);
|
||||
return error_string(buf, end, "pK-error", spec);
|
||||
|
Loading…
Reference in New Issue
Block a user