Skip to content

Commit

Permalink
serial: samsung: fix potential soft lockup during uart write
Browse files Browse the repository at this point in the history
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: Kyungmin Park <kyungmin.park@samsung.com>
Reported-by: Hyeonkook Kim <hk619.kim@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Thomas Abraham authored and Greg Kroah-Hartman committed Nov 26, 2012
1 parent daa74a2 commit c15c374
Showing 1 changed file with 12 additions and 4 deletions.
16 changes: 12 additions & 4 deletions drivers/tty/serial/samsung.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
struct uart_port *port = &ourport->port;
struct tty_struct *tty = port->state->port.tty;
unsigned int ufcon, ch, flag, ufstat, uerstat;
unsigned long flags;
int max_count = 64;

spin_lock_irqsave(&port->lock, flags);

while (max_count-- > 0) {
ufcon = rd_regl(port, S3C2410_UFCON);
ufstat = rd_regl(port, S3C2410_UFSTAT);
Expand Down Expand Up @@ -299,6 +302,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
tty_flip_buffer_push(tty);

out:
spin_unlock_irqrestore(&port->lock, flags);
return IRQ_HANDLED;
}

Expand All @@ -307,8 +311,11 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit;
unsigned long flags;
int count = 256;

spin_lock_irqsave(&port->lock, flags);

if (port->x_char) {
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
Expand Down Expand Up @@ -336,13 +343,17 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
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);
spin_lock(&port->lock);
}

if (uart_circ_empty(xmit))
s3c24xx_serial_stop_tx(port);

out:
spin_unlock_irqrestore(&port->lock, flags);
return IRQ_HANDLED;
}

Expand All @@ -352,10 +363,8 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
unsigned int pend = rd_regl(port, S3C64XX_UINTP);
unsigned long flags;
irqreturn_t ret = IRQ_HANDLED;

spin_lock_irqsave(&port->lock, flags);
if (pend & S3C64XX_UINTM_RXD_MSK) {
ret = s3c24xx_serial_rx_chars(irq, id);
wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);
Expand All @@ -364,7 +373,6 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
ret = s3c24xx_serial_tx_chars(irq, id);
wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);
}
spin_unlock_irqrestore(&port->lock, flags);
return ret;
}

Expand Down

0 comments on commit c15c374

Please sign in to comment.