Commit bedb404e authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Greg Kroah-Hartman

serial: 8250_port: Don't use power management for kernel console

Doing any kind of power management for kernel console is really bad idea.

First of all, it runs in poll and atomic mode. This fact attaches a limitation
on the functions that might be called. For example, pm_runtime_get_sync() might
sleep and thus can't be used. This call needs, for example, to bring the device
to powered on state on the system, where the power on sequence may require
on-atomic operations, such as Intel Cherrytrail with ACPI enumerated UARTs.
That said, on ACPI enabled platforms it might even call firmware for a job.

On the other hand pm_runtime_get() doesn't guarantee that device will become
powered on fast enough.

Besides that, imagine the case when console is about to print a kernel Oops and
it's powered off. In such an emergency case calling the complex functions is
not the best what we can do, taking into consideration that user wants to see
at least something of the last kernel word before it passes away.

Here we modify the 8250 console code to prevent runtime power management.

Note, there is a behaviour change for OMAP boards. It will require to detach
kernel console to become idle.

Link: https://lists.openwall.net/linux-kernel/2018/09/29/65Suggested-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20200217114016.49856-6-andriy.shevchenko@linux.intel.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a3cb39d2
...@@ -608,6 +608,14 @@ static int univ8250_console_setup(struct console *co, char *options) ...@@ -608,6 +608,14 @@ static int univ8250_console_setup(struct console *co, char *options)
return retval; return retval;
} }
static int univ8250_console_exit(struct console *co)
{
struct uart_port *port;
port = &serial8250_ports[co->index].port;
return serial8250_console_exit(port);
}
/** /**
* univ8250_console_match - non-standard console matching * univ8250_console_match - non-standard console matching
* @co: registering console * @co: registering console
...@@ -666,6 +674,7 @@ static struct console univ8250_console = { ...@@ -666,6 +674,7 @@ static struct console univ8250_console = {
.write = univ8250_console_write, .write = univ8250_console_write,
.device = uart_console_device, .device = uart_console_device,
.setup = univ8250_console_setup, .setup = univ8250_console_setup,
.exit = univ8250_console_exit,
.match = univ8250_console_match, .match = univ8250_console_match,
.flags = CON_PRINTBUFFER | CON_ANYTIME, .flags = CON_PRINTBUFFER | CON_ANYTIME,
.index = -1, .index = -1,
......
...@@ -3200,6 +3200,9 @@ static void serial8250_console_restore(struct uart_8250_port *up) ...@@ -3200,6 +3200,9 @@ static void serial8250_console_restore(struct uart_8250_port *up)
* any possible real use of the port... * any possible real use of the port...
* *
* The console_lock must be held when we get here. * The console_lock must be held when we get here.
*
* Doing runtime PM is really a bad idea for the kernel console.
* Thus, we assume the function is called when device is powered up.
*/ */
void serial8250_console_write(struct uart_8250_port *up, const char *s, void serial8250_console_write(struct uart_8250_port *up, const char *s,
unsigned int count) unsigned int count)
...@@ -3212,8 +3215,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, ...@@ -3212,8 +3215,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
touch_nmi_watchdog(); touch_nmi_watchdog();
serial8250_rpm_get(up);
if (oops_in_progress) if (oops_in_progress)
locked = spin_trylock_irqsave(&port->lock, flags); locked = spin_trylock_irqsave(&port->lock, flags);
else else
...@@ -3268,7 +3269,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, ...@@ -3268,7 +3269,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
if (locked) if (locked)
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
serial8250_rpm_put(up);
} }
static unsigned int probe_baud(struct uart_port *port) static unsigned int probe_baud(struct uart_port *port)
...@@ -3292,6 +3292,7 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) ...@@ -3292,6 +3292,7 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
int bits = 8; int bits = 8;
int parity = 'n'; int parity = 'n';
int flow = 'n'; int flow = 'n';
int ret;
if (!port->iobase && !port->membase) if (!port->iobase && !port->membase)
return -ENODEV; return -ENODEV;
...@@ -3301,7 +3302,22 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) ...@@ -3301,7 +3302,22 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
else if (probe) else if (probe)
baud = probe_baud(port); baud = probe_baud(port);
return uart_set_options(port, port->cons, baud, parity, bits, flow); ret = uart_set_options(port, port->cons, baud, parity, bits, flow);
if (ret)
return ret;
if (port->dev)
pm_runtime_get_sync(port->dev);
return 0;
}
int serial8250_console_exit(struct uart_port *port)
{
if (port->dev)
pm_runtime_put_sync(port->dev);
return 0;
} }
#endif /* CONFIG_SERIAL_8250_CONSOLE */ #endif /* CONFIG_SERIAL_8250_CONSOLE */
......
...@@ -179,6 +179,7 @@ void serial8250_set_defaults(struct uart_8250_port *up); ...@@ -179,6 +179,7 @@ void serial8250_set_defaults(struct uart_8250_port *up);
void serial8250_console_write(struct uart_8250_port *up, const char *s, void serial8250_console_write(struct uart_8250_port *up, const char *s,
unsigned int count); unsigned int count);
int serial8250_console_setup(struct uart_port *port, char *options, bool probe); int serial8250_console_setup(struct uart_port *port, char *options, bool probe);
int serial8250_console_exit(struct uart_port *port);
extern void serial8250_set_isa_configurator(void (*v) extern void serial8250_set_isa_configurator(void (*v)
(int port, struct uart_port *up, (int port, struct uart_port *up,
......
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