Skip to content

Commit bedb404

Browse files
andy-shevgregkh
authored andcommitted
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/65 Suggested-by: Russell King <[email protected]> Signed-off-by: Andy Shevchenko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a3cb39d commit bedb404

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

drivers/tty/serial/8250/8250_core.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,14 @@ static int univ8250_console_setup(struct console *co, char *options)
608608
return retval;
609609
}
610610

611+
static int univ8250_console_exit(struct console *co)
612+
{
613+
struct uart_port *port;
614+
615+
port = &serial8250_ports[co->index].port;
616+
return serial8250_console_exit(port);
617+
}
618+
611619
/**
612620
* univ8250_console_match - non-standard console matching
613621
* @co: registering console
@@ -666,6 +674,7 @@ static struct console univ8250_console = {
666674
.write = univ8250_console_write,
667675
.device = uart_console_device,
668676
.setup = univ8250_console_setup,
677+
.exit = univ8250_console_exit,
669678
.match = univ8250_console_match,
670679
.flags = CON_PRINTBUFFER | CON_ANYTIME,
671680
.index = -1,

drivers/tty/serial/8250/8250_port.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3200,6 +3200,9 @@ static void serial8250_console_restore(struct uart_8250_port *up)
32003200
* any possible real use of the port...
32013201
*
32023202
* The console_lock must be held when we get here.
3203+
*
3204+
* Doing runtime PM is really a bad idea for the kernel console.
3205+
* Thus, we assume the function is called when device is powered up.
32033206
*/
32043207
void serial8250_console_write(struct uart_8250_port *up, const char *s,
32053208
unsigned int count)
@@ -3212,8 +3215,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
32123215

32133216
touch_nmi_watchdog();
32143217

3215-
serial8250_rpm_get(up);
3216-
32173218
if (oops_in_progress)
32183219
locked = spin_trylock_irqsave(&port->lock, flags);
32193220
else
@@ -3268,7 +3269,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
32683269

32693270
if (locked)
32703271
spin_unlock_irqrestore(&port->lock, flags);
3271-
serial8250_rpm_put(up);
32723272
}
32733273

32743274
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)
32923292
int bits = 8;
32933293
int parity = 'n';
32943294
int flow = 'n';
3295+
int ret;
32953296

32963297
if (!port->iobase && !port->membase)
32973298
return -ENODEV;
@@ -3301,7 +3302,22 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
33013302
else if (probe)
33023303
baud = probe_baud(port);
33033304

3304-
return uart_set_options(port, port->cons, baud, parity, bits, flow);
3305+
ret = uart_set_options(port, port->cons, baud, parity, bits, flow);
3306+
if (ret)
3307+
return ret;
3308+
3309+
if (port->dev)
3310+
pm_runtime_get_sync(port->dev);
3311+
3312+
return 0;
3313+
}
3314+
3315+
int serial8250_console_exit(struct uart_port *port)
3316+
{
3317+
if (port->dev)
3318+
pm_runtime_put_sync(port->dev);
3319+
3320+
return 0;
33053321
}
33063322

33073323
#endif /* CONFIG_SERIAL_8250_CONSOLE */

include/linux/serial_8250.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ void serial8250_set_defaults(struct uart_8250_port *up);
179179
void serial8250_console_write(struct uart_8250_port *up, const char *s,
180180
unsigned int count);
181181
int serial8250_console_setup(struct uart_port *port, char *options, bool probe);
182+
int serial8250_console_exit(struct uart_port *port);
182183

183184
extern void serial8250_set_isa_configurator(void (*v)
184185
(int port, struct uart_port *up,

0 commit comments

Comments
 (0)