message on the debug console.Freeing unused kernel memory:
I have tracked this down to the atmel_serial driver used for the debug console. When the kernel execs /sbin/init from kernel_init_freeable(), it opens the /dev/console device and dups this to stdout and stderr (3 references). /sbin/init (init.sysvinit on my systems) ends up closing stdin, stdout, and stderr. These actions have the following effects.
1) the first open in kernel_init_freeable() invokes atmel_startup() in atmel_serial.c.
2) the last close from init invokes atmel_shutdown().
In atmel_shutdown(), it calls atmel_stop_tx(). atmel_stop_tx() disables the UART transmitter. This action prevents any further kernel output (printk) from working, and causes them to hang the kernel.
It seems that the UART transmitter should not be disabled in atmel_stop_tx() if the UART is also being used as a console device by the kernel. Indeed, I can correct the hang by commenting out the line of code that disables the UART transmitter in atmel_stop_tx(). This is specifically on the dbgu UART in the atmel_serial driver.
The following patch to atmel_serial.c seems to fix this bug.
Code: Select all
Index: drivers/tty/serial/atmel_serial.c
===================================================================
--- drivers/tty/serial/atmel_serial.c (revision 4602)
+++ drivers/tty/serial/atmel_serial.c (working copy)
@@ -73,10 +73,13 @@
#include "serial_mctrl_gpio.h"
static void atmel_start_rx(struct uart_port *port);
static void atmel_stop_rx(struct uart_port *port);
+#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
+static inline bool atmel_is_console_port(struct uart_port *port);
+#endif
#ifdef CONFIG_SERIAL_ATMEL_TTYAT
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
* should coexist with the 8250 driver, such as if we have an external 16C550
@@ -2002,10 +2005,17 @@ static void atmel_shutdown(struct uart_p
free_irq(port->irq, port);
atmel_port->ms_irq_enabled = false;
atmel_flush_buffer(port);
+
+#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
+ /* Reenable the transmitter and receiver for console device. */
+ if (atmel_is_console_port(port)) {
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
+ }
+#endif
}
/*
* Power / Clock management.
*/