Commit c15c3747 authored by Thomas Abraham's avatar Thomas Abraham Committed by Greg Kroah-Hartman

serial: samsung: fix potential soft lockup during uart write

Certain tty line discipline implementations such slip and bluetooth hci invoke
the serial core uart_write() api in their write_wakeup callback. This leads to
a soft lockup with samsung serial driver since the uart port lock is taken in
the driver's interrupt handler and uart_write() attempts to take the same lock
again.

Fix this issue by releasing the uart port lock before the call to
uart_write_wakeup() in the tx handler. Also move the spin-lock/unlock sequence
from s3c64xx_serial_handle_irq() function into the tx and rx irq handlers so
that this change is applicable to s3c24xx platforms as well.
Reported-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Reported-by: default avatarHyeonkook Kim <hk619.kim@samsung.com>
Signed-off-by: default avatarThomas Abraham <thomas.abraham@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent daa74a25
...@@ -223,8 +223,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ...@@ -223,8 +223,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
struct uart_port *port = &ourport->port; struct uart_port *port = &ourport->port;
struct tty_struct *tty = port->state->port.tty; struct tty_struct *tty = port->state->port.tty;
unsigned int ufcon, ch, flag, ufstat, uerstat; unsigned int ufcon, ch, flag, ufstat, uerstat;
unsigned long flags;
int max_count = 64; int max_count = 64;
spin_lock_irqsave(&port->lock, flags);
while (max_count-- > 0) { while (max_count-- > 0) {
ufcon = rd_regl(port, S3C2410_UFCON); ufcon = rd_regl(port, S3C2410_UFCON);
ufstat = rd_regl(port, S3C2410_UFSTAT); ufstat = rd_regl(port, S3C2410_UFSTAT);
...@@ -299,6 +302,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ...@@ -299,6 +302,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
tty_flip_buffer_push(tty); tty_flip_buffer_push(tty);
out: out:
spin_unlock_irqrestore(&port->lock, flags);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -307,8 +311,11 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) ...@@ -307,8 +311,11 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
struct s3c24xx_uart_port *ourport = id; struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port; struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit; struct circ_buf *xmit = &port->state->xmit;
unsigned long flags;
int count = 256; int count = 256;
spin_lock_irqsave(&port->lock, flags);
if (port->x_char) { if (port->x_char) {
wr_regb(port, S3C2410_UTXH, port->x_char); wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++; port->icount.tx++;
...@@ -336,13 +343,17 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) ...@@ -336,13 +343,17 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
port->icount.tx++; port->icount.tx++;
} }
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
spin_unlock(&port->lock);
uart_write_wakeup(port); uart_write_wakeup(port);
spin_lock(&port->lock);
}
if (uart_circ_empty(xmit)) if (uart_circ_empty(xmit))
s3c24xx_serial_stop_tx(port); s3c24xx_serial_stop_tx(port);
out: out:
spin_unlock_irqrestore(&port->lock, flags);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -352,10 +363,8 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) ...@@ -352,10 +363,8 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
struct s3c24xx_uart_port *ourport = id; struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port; struct uart_port *port = &ourport->port;
unsigned int pend = rd_regl(port, S3C64XX_UINTP); unsigned int pend = rd_regl(port, S3C64XX_UINTP);
unsigned long flags;
irqreturn_t ret = IRQ_HANDLED; irqreturn_t ret = IRQ_HANDLED;
spin_lock_irqsave(&port->lock, flags);
if (pend & S3C64XX_UINTM_RXD_MSK) { if (pend & S3C64XX_UINTM_RXD_MSK) {
ret = s3c24xx_serial_rx_chars(irq, id); ret = s3c24xx_serial_rx_chars(irq, id);
wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);
...@@ -364,7 +373,6 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) ...@@ -364,7 +373,6 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
ret = s3c24xx_serial_tx_chars(irq, id); ret = s3c24xx_serial_tx_chars(irq, id);
wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);
} }
spin_unlock_irqrestore(&port->lock, flags);
return ret; return ret;
} }
......
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