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: 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>
This commit is contained in:
parent
daa74a25cc
commit
c15c3747ee
1 changed files with 12 additions and 4 deletions
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue