Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
[sfrench/cifs-2.6.git] / drivers / tty / serial / serial_core.c
index c439a5a1e6c078e71bda4ea756480b1344507707..d4cca5bdaf1c60d40ba45b6c1815f2a6d98046af 100644 (file)
@@ -205,10 +205,15 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
        if (!state->xmit.buf) {
                state->xmit.buf = (unsigned char *) page;
                uart_circ_clear(&state->xmit);
+               uart_port_unlock(uport, flags);
        } else {
+               uart_port_unlock(uport, flags);
+               /*
+                * Do not free() the page under the port lock, see
+                * uart_shutdown().
+                */
                free_page(page);
        }
-       uart_port_unlock(uport, flags);
 
        retval = uport->ops->startup(uport);
        if (retval == 0) {
@@ -268,6 +273,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
        struct uart_port *uport = uart_port_check(state);
        struct tty_port *port = &state->port;
        unsigned long flags = 0;
+       char *xmit_buf = NULL;
 
        /*
         * Set the TTY IO error marker
@@ -298,14 +304,18 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
        tty_port_set_suspended(port, 0);
 
        /*
-        * Free the transmit buffer page.
+        * Do not free() the transmit buffer page under the port lock since
+        * this can create various circular locking scenarios. For instance,
+        * console driver may need to allocate/free a debug object, which
+        * can endup in printk() recursion.
         */
        uart_port_lock(state, flags);
-       if (state->xmit.buf) {
-               free_page((unsigned long)state->xmit.buf);
-               state->xmit.buf = NULL;
-       }
+       xmit_buf = state->xmit.buf;
+       state->xmit.buf = NULL;
        uart_port_unlock(uport, flags);
+
+       if (xmit_buf)
+               free_page((unsigned long)xmit_buf);
 }
 
 /**