Commit 98d0052d authored by Linus Torvalds's avatar Linus Torvalds

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()
  ...
parents 73fa58dc 6b2b0d83
......@@ -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,6 +945,7 @@ static void __exit cleanup_netconsole(void)
{
struct netconsole_target *nt, *tmp;
if (console_is_registered(&netconsole_ext))
unregister_console(&netconsole_ext);
unregister_console(&netconsole);
dynamic_netconsole_exit();
......
......@@ -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,8 +484,13 @@ 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)
break;
}
console_srcu_read_unlock(cookie);
if (con)
return;
already_warned = true;
......@@ -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;
};
#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.
*/
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);
}
/*
* for_each_console() allows you to iterate on each console
* 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) \
for (con = console_drivers; con != NULL; con = con->next)
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)
......
This diff is collapsed.
......@@ -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);
}
rhp = kmalloc(sizeof(*rhp), GFP_KERNEL);
if (!WARN_ON_ONCE(!rhp))
kfree_rcu(rhp, rh);
......@@ -585,12 +583,10 @@ 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));
}
}
if (rcu_self_test_counter != early_boot_test_counter) {
WARN_ON(1);
ret = -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment